json.c (9457B)
1 2 #include "jsmn.h" 3 #include "json.h" 4 #include <stdio.h> 5 #include <assert.h> 6 #include <stdio.h> 7 #include <ctype.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #define ERROR_READING_CHANNELS -2 12 #define ERROR_ALLOCATING_CHANNELS -3 13 14 struct channel_parser { 15 const char *field; 16 enum channel_parsing_state state; 17 }; 18 19 20 struct node_parser { 21 const char *field; 22 enum node_parsing_state state; 23 }; 24 25 /* "source": "02fc7ef232bd958209a2180fb18844388babc48a81d31434e343ecc4d7dd1a9186", */ 26 /* "destination": "03b7ca940bc33b882dc1f1bee353a6cf205b1a7472d8ae24d45370a8f510c27d23", */ 27 /* "short_channel_id": "505273:1478:0", */ 28 /* "public": true, */ 29 /* "satoshis": 80000, */ 30 /* "flags": 0, */ 31 /* "active": true, */ 32 /* "last_update": 1531178014, */ 33 /* "base_fee_millisatoshi": 1, */ 34 /* "fee_per_millionth": 1, */ 35 /* "delay": 14 */ 36 static const struct channel_parser channel_parsers[] = { 37 { "source", PARSING_CHAN_SOURCE }, 38 { "destination", PARSING_CHAN_DESTINATION }, 39 { "short_channel_id", PARSING_CHAN_SHORTID }, 40 { "public", PARSING_CHAN_PUBLIC }, 41 { "satoshis", PARSING_CHAN_SATOSHIS }, 42 { "channel_flags", PARSING_CHAN_FLAGS }, 43 { "active", PARSING_CHAN_ACTIVE }, 44 { "last_update", PARSING_CHAN_LAST_UPDATE }, 45 { "base_fee_millisatoshi", PARSING_CHAN_BASE_FEE }, 46 { "fee_per_millionth", PARSING_CHAN_FEE_PER }, 47 { "delay", PARSING_CHAN_DELAY }, 48 }; 49 50 /* "nodeid": "03a8334aba5660e241468e2f0deb2526bfd50d0e3fe808d882913e39094dc1a028", */ 51 /* "alias": "cosmicApotheosis", */ 52 /* "color": "33cccc", */ 53 /* "last_timestamp": 1533693585, */ 54 /* "global_features": "", */ 55 /* "addresses": [ */ 56 /* { "type": "ipv4", */ 57 /* "address": "138.229.205.237", */ 58 /* "port": 9735 */ 59 /* }] */ 60 static const struct node_parser node_parsers[] = { 61 { "nodeid", PARSING_NODE_ID }, 62 { "alias", PARSING_NODE_ALIAS }, 63 { "color", PARSING_NODE_COLOR }, 64 { "addresses", PARSING_NODE_ADDRESSES }, 65 }; 66 67 #define MKTMPBUF \ 68 char buf[toklen + 1]; \ 69 buf[toklen] = '\0'; \ 70 memcpy(buf, tokstr, toklen) 71 72 static inline u32 parse_u32(int toklen, const char *tokstr) 73 { 74 MKTMPBUF; 75 return strtoul(buf, NULL, 10); 76 } 77 78 static inline u64 parse_u64(int toklen, const char *tokstr) 79 { 80 MKTMPBUF; 81 return strtoll(buf, NULL, 10); 82 } 83 84 static int parse_short_channel_id(int toklen, const char *tokstr, 85 struct short_channel_id *chanid) 86 { 87 char buf[toklen + 1]; 88 buf[toklen] = '\0'; 89 memcpy(buf, tokstr, toklen); 90 return sscanf(buf, "%ux%ux%hu", 91 &chanid->blocknum, 92 &chanid->txnum, 93 &chanid->outnum); 94 } 95 96 static inline int tokeq(size_t toklen, const char* tok, const char *str) 97 { 98 if (toklen != strlen(str)) 99 return 0; 100 return memcmp(tok, str, toklen) == 0; 101 } 102 103 int parse_json(FILE *fd, jsmntok_t **ptoks, int *ntoks, char **pbuffer) { 104 int off, parserr, i; 105 void *res; 106 char *buffer; 107 jsmntok_t *toks; 108 jsmn_parser parser; 109 jsmn_init(&parser); 110 111 int bufsize = 4096 * 4096; 112 int toksize = 512 * 4096; 113 114 buffer = malloc(bufsize); 115 toks = calloc(toksize, sizeof(jsmntok_t)); 116 117 off = 0; 118 parserr = 0; 119 120 while (parserr <= 0) { 121 /* Read more if parser says, or we have 0 tokens. */ 122 if (parserr == 0 || parserr == JSMN_ERROR_PART) { 123 i = fread(buffer + off, 1, bufsize - 1 - off, fd); 124 if (i <= 0) 125 return ERROR_READING_CHANNELS; 126 off += i; 127 /* NUL terminate */ 128 buffer[off] = '\0'; 129 } 130 131 /* (Continue) parsing */ 132 parserr = jsmn_parse(&parser, buffer, off, toks, toksize); 133 134 switch (parserr) { 135 case JSMN_ERROR_INVAL: 136 return ERROR_READING_CHANNELS; 137 case JSMN_ERROR_NOMEM: 138 /* Need more tokens, double it */ 139 toksize *= 2; 140 res = realloc(toks, toksize * sizeof(jsmntok_t)); 141 if (res == NULL) { 142 free(toks); 143 return ERROR_ALLOCATING_CHANNELS; 144 } 145 toks = res; 146 break; 147 case JSMN_ERROR_PART: 148 /* Need more data: make room if necessary */ 149 if (off == bufsize - 1) { 150 bufsize *= 2; 151 res = realloc(buffer, bufsize); 152 if (res == NULL) { 153 free(buffer); 154 return ERROR_ALLOCATING_CHANNELS; 155 } 156 buffer = res; 157 } 158 break; 159 } 160 } 161 162 *ntoks = parserr; 163 *ptoks = toks; 164 *pbuffer = buffer; 165 166 return 0; 167 } 168 169 170 static inline u8 hexdigit(char hex) { 171 return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ; 172 } 173 174 175 static void parse_color(int toklen, const char *tokstr, union color *color) { 176 color->a = 1.0f; 177 178 if (toklen != 6) 179 goto colorerr; 180 181 for (int i = 0; i < 3; i++) { 182 u8 c1 = tokstr[i*2]; 183 u8 c2 = tokstr[i*2+1]; 184 185 if (!isxdigit(c1) || !isxdigit(c2)) 186 goto colorerr; 187 188 u8 val = hexdigit(c1) << 4 | hexdigit(c2); 189 color->rgba[i] = ((float)val) / 255.0f; 190 } 191 192 return; 193 colorerr: 194 color->r = 0.0f; 195 color->g = 0.0f; 196 color->b = 0.0f; 197 return; 198 } 199 200 int parse_clightning_nodes(FILE *fd, int *node_count, struct node **pnodes) 201 { 202 struct node *node = NULL, *nodes; 203 char *buffer, *tokstr; 204 void *res; 205 jsmntok_t *toks; 206 jsmntok_t *tok; 207 int i, ntoks = 0, toklen = 0, objs = 0; 208 int nodecap = 4096; 209 enum node_parsing_state state = PARSING_NODE_TOKEN; 210 211 *node_count = 0; 212 213 nodes = calloc(nodecap, sizeof(struct node)); 214 215 int acount = 0; 216 217 int rez = parse_json(fd, &toks, &ntoks, &buffer); 218 if (rez < 0) 219 return rez; 220 221 for (i = 0; i < ntoks; i++) { 222 tok = &toks[i]; 223 224 if (tok->type == JSMN_ARRAY) { 225 if (state == PARSING_NODE_ADDRESSES) { 226 if (++acount == 1) { 227 acount = 0; 228 state = PARSING_NODE_TOKEN; 229 } 230 } 231 continue; 232 } 233 234 if (tok->type == JSMN_OBJECT) { 235 if (state == PARSING_NODE_ADDRESSES) 236 continue; 237 238 if (++objs > 2) 239 (*node_count)++; 240 241 continue; 242 } 243 244 toklen = tok->end - tok->start; 245 tokstr = &buffer[tok->start]; 246 247 node = &nodes[*node_count]; 248 249 // allocate more channels if needed 250 if (*node_count > nodecap) { 251 nodecap *= 2; 252 res = realloc(nodes, nodecap * sizeof(struct channel)); 253 if (res == NULL) { 254 free(nodes); 255 return ERROR_ALLOCATING_CHANNELS; 256 } 257 nodes = res; 258 } 259 260 switch (state) { 261 case PARSING_NODE_ID: 262 strncpy(node->id, tokstr, min(toklen, PUBKEY_SIZE)); 263 state = PARSING_NODE_TOKEN; 264 break; 265 266 case PARSING_NODE_ALIAS: 267 strncpy(node->alias, tokstr, min(toklen, MAX_ALIAS_SIZE)); 268 state = PARSING_NODE_TOKEN; 269 break; 270 271 case PARSING_NODE_COLOR: 272 parse_color(toklen, tokstr, &node->color); 273 state = PARSING_NODE_TOKEN; 274 break; 275 276 case PARSING_NODE_ADDRESSES: 277 // TODO: parse addresses 278 state = PARSING_NODE_TOKEN; 279 break; 280 281 case PARSING_NODE_TOKEN: 282 for (size_t i = 0; i < ARRAY_SIZE(node_parsers); i++) { 283 if (tokeq(toklen, tokstr, 284 node_parsers[i].field)) { 285 state = node_parsers[i].state; 286 continue; 287 } 288 } 289 break; 290 } 291 292 } 293 294 *pnodes = nodes; 295 *node_count = *node_count + 1; 296 297 return 0; 298 } 299 300 301 int parse_clightning_channels(FILE *fd, int *nchans, struct channel **pchannels) 302 { 303 char *buffer; 304 void *res; 305 struct channel *chan = NULL, *channels; 306 struct short_channel_id last_chan; 307 int chancap = 4096; 308 jsmntok_t *toks; 309 jsmntok_t *tok; 310 char *tokstr; 311 int ntoks = 0, toklen = 0, objs = 0; 312 enum channel_parsing_state state = PARSING_CHAN_TOKEN; 313 314 int rez = parse_json(fd, &toks, &ntoks, &buffer); 315 if (rez < 0) 316 return rez; 317 318 channels = calloc(chancap, sizeof(struct channel)); 319 320 // parse tokens 321 for (int i = 0; i < ntoks; ++i) { 322 tok = &toks[i]; 323 324 if (tok->type == JSMN_ARRAY) 325 continue; 326 327 if (tok->type == JSMN_OBJECT) { 328 if (++objs > 2) 329 (*nchans)++; 330 331 continue; 332 } 333 334 toklen = tok->end - tok->start; 335 tokstr = &buffer[tok->start]; 336 337 // allocate more channels if needed 338 if (*nchans >= chancap) { 339 chancap *= 2; 340 res = realloc(channels, chancap * sizeof(struct channel)); 341 if (res == NULL) { 342 free(channels); 343 return ERROR_ALLOCATING_CHANNELS; 344 } 345 channels = res; 346 } 347 348 chan = &channels[*nchans]; 349 350 switch (state) { 351 case PARSING_CHAN_SOURCE: 352 strncpy(chan->source, tokstr, min(PUBKEY_SIZE, toklen)); 353 state = PARSING_CHAN_TOKEN; 354 break; 355 356 case PARSING_CHAN_DESTINATION: 357 strncpy(chan->destination, tokstr, min(PUBKEY_SIZE, toklen)); 358 state = PARSING_CHAN_TOKEN; 359 break; 360 361 case PARSING_CHAN_SHORTID: 362 parse_short_channel_id(toklen, tokstr, 363 &chan->short_channel_id); 364 state = PARSING_CHAN_TOKEN; 365 break; 366 367 case PARSING_CHAN_PUBLIC: 368 chan->public = tokeq(toklen, tokstr, "true"); 369 state = PARSING_CHAN_TOKEN; 370 break; 371 372 case PARSING_CHAN_SATOSHIS: 373 chan->satoshis = parse_u64(toklen, tokstr); 374 state = PARSING_CHAN_TOKEN; 375 break; 376 377 case PARSING_CHAN_FLAGS: 378 chan->flags = parse_u32(toklen, tokstr); 379 state = PARSING_CHAN_TOKEN; 380 break; 381 382 case PARSING_CHAN_ACTIVE: 383 chan->active = tokeq(toklen, tokstr, "true"); 384 state = PARSING_CHAN_TOKEN; 385 break; 386 387 case PARSING_CHAN_LAST_UPDATE: 388 chan->last_update_timestamp = parse_u32(toklen, tokstr); 389 state = PARSING_CHAN_TOKEN; 390 break; 391 392 case PARSING_CHAN_BASE_FEE: 393 chan->base_fee_msat = parse_u32(toklen, tokstr); 394 state = PARSING_CHAN_TOKEN; 395 break; 396 397 case PARSING_CHAN_FEE_PER: 398 chan->fee_per_millionth = parse_u32(toklen, tokstr); 399 state = PARSING_CHAN_TOKEN; 400 break; 401 402 case PARSING_CHAN_DELAY: 403 chan->delay = parse_u32(toklen, tokstr); 404 state = PARSING_CHAN_TOKEN; 405 break; 406 407 case PARSING_CHAN_TOKEN: 408 for (size_t i = 0; i < ARRAY_SIZE(channel_parsers); i++) { 409 if (tokeq(toklen, tokstr, 410 channel_parsers[i].field)) { 411 state = channel_parsers[i].state; 412 continue; 413 } 414 } 415 break; 416 } 417 418 } 419 420 *pchannels = channels; 421 *nchans = *nchans + 1; 422 423 free(buffer); 424 free(toks); 425 426 return 0; 427 }