jsonrpc.c (11860B)
1 /*
2 * Copyright (c) 2016 Raphaƫl Monrouzeau <raphael.monrouzeau@gmail.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <limits.h>
18 #include <stdbool.h>
19
20 #include <yajl/yajl_tree.h>
21 #include <yajl/yajl_gen.h>
22
23 #include "kore.h"
24 #include "http.h"
25 #include "jsonrpc.h"
26
27 static void
28 init_log(struct jsonrpc_log *log)
29 {
30 log->msg = NULL;
31 log->next = log;
32 log->prev = log;
33 }
34
35 static void
36 append_log(struct jsonrpc_log *prev, int lvl, char *msg)
37 {
38 struct jsonrpc_log *new = kore_malloc(sizeof(struct jsonrpc_log));
39
40 new->lvl = lvl;
41 new->msg = msg;
42
43 new->prev = prev;
44 new->next = prev->next;
45 prev->next->prev = new;
46 prev->next = new;
47 }
48
49 static void
50 free_log(struct jsonrpc_log *root)
51 {
52 for (struct jsonrpc_log *it = root->next; it != root; it = it->next) {
53 kore_free(it);
54 }
55 }
56
57 static void
58 init_request(struct jsonrpc_request *req)
59 {
60 init_log(&req->log);
61 kore_buf_init(&req->buf, 256);
62 req->gen = NULL;
63 req->http = NULL;
64 req->json = NULL;
65 req->id = NULL;
66 req->method = NULL;
67 req->params = NULL;
68 req->log_levels = (1 << LOG_EMERG) | (1 << LOG_ERR) | (1 << LOG_WARNING)
69 | (1 << LOG_NOTICE);
70 req->flags = 0;
71 }
72
73 void
74 jsonrpc_destroy_request(struct jsonrpc_request *req)
75 {
76 if (req->gen != NULL) {
77 yajl_gen_free(req->gen);
78 req->gen = NULL;
79 }
80 if (req->json != NULL) {
81 yajl_tree_free(req->json);
82 req->json = NULL;
83 }
84 kore_buf_cleanup(&req->buf);
85 free_log(&req->log);
86 }
87
88 void
89 jsonrpc_log(struct jsonrpc_request *req, int lvl, const char *fmt, ...)
90 {
91 va_list ap;
92 char *msg;
93 size_t start = req->buf.offset;
94
95 va_start(ap, fmt);
96 kore_buf_appendv(&req->buf, fmt, ap);
97 va_end(ap);
98
99 msg = kore_buf_stringify(&req->buf, NULL) + start;
100
101 append_log(&req->log, lvl, msg);
102 }
103
104 static int
105 read_json_body(struct http_request *http_req, struct jsonrpc_request *req)
106 {
107 char *body_string;
108 ssize_t body_len = 0, chunk_len;
109 u_int8_t chunk_buffer[BUFSIZ];
110 char error_buffer[1024];
111
112 for (;;) {
113 chunk_len = http_body_read(http_req, chunk_buffer,
114 sizeof(chunk_buffer));
115 if (chunk_len == -1) {
116 jsonrpc_log(req, LOG_CRIT,
117 "Failed to read request body");
118 return (JSONRPC_SERVER_ERROR);
119 }
120
121 if (chunk_len == 0)
122 break;
123
124 if (body_len > SSIZE_MAX - chunk_len) {
125 jsonrpc_log(req, LOG_CRIT,
126 "Request body bigger than the platform accepts");
127 return (JSONRPC_SERVER_ERROR);
128 }
129 body_len += chunk_len;
130
131 kore_buf_append(&req->buf, chunk_buffer, chunk_len);
132 }
133
134 /* Grab our body data as a NUL-terminated string. */
135 body_string = kore_buf_stringify(&req->buf, NULL);
136
137 /* Parse the body via yajl now. */
138 *error_buffer = 0;
139 req->json = yajl_tree_parse(body_string, error_buffer,
140 sizeof(error_buffer));
141 if (req->json == NULL) {
142 if (strlen(error_buffer)) {
143 jsonrpc_log(req, LOG_ERR, "Invalid json: %s",
144 error_buffer);
145 } else {
146 jsonrpc_log(req, LOG_ERR, "Invalid json");
147 }
148 return (JSONRPC_PARSE_ERROR);
149 }
150
151 return (0);
152 }
153
154 static int
155 parse_json_body(struct jsonrpc_request *req)
156 {
157 static const char *proto_path[] = { "jsonrpc", NULL };
158 static const char *id_path[] = { "id", NULL };
159 static const char *method_path[] = { "method", NULL };
160 static const char *params_path[] = { "params", NULL };
161
162 /* Check protocol first. */
163 yajl_val proto = yajl_tree_get(req->json, proto_path, yajl_t_string);
164 if (proto == NULL) {
165 jsonrpc_log(req, LOG_ERR,
166 "JSON-RPC protocol MUST be indicated and \"2.0\"");
167 return (JSONRPC_PARSE_ERROR);
168 }
169
170 char *proto_string = YAJL_GET_STRING(proto);
171 if (proto_string == NULL) {
172 jsonrpc_log(req, LOG_ERR,
173 "JSON-RPC protocol MUST be indicated and \"2.0\"");
174 return (JSONRPC_PARSE_ERROR);
175 }
176
177 if (strcmp("2.0", proto_string) != 0) {
178 jsonrpc_log(req, LOG_ERR,
179 "JSON-RPC protocol MUST be indicated and \"2.0\"");
180 return (JSONRPC_PARSE_ERROR);
181 }
182
183 /* Check id. */
184 if ((req->id = yajl_tree_get(req->json, id_path, yajl_t_any)) != NULL) {
185 if (YAJL_IS_NUMBER(req->id)) {
186 if (!YAJL_IS_INTEGER(req->id)) {
187 jsonrpc_log(req, LOG_ERR,
188 "JSON-RPC id SHOULD NOT contain fractional"
189 " parts");
190 return (JSONRPC_PARSE_ERROR);
191 }
192 } else if (!YAJL_IS_STRING(req->id)) {
193 jsonrpc_log(req, LOG_ERR,
194 "JSON-RPC id MUST contain a String or Number");
195 return (JSONRPC_PARSE_ERROR);
196 }
197 }
198
199 /* Check method. */
200 if ((req->method = YAJL_GET_STRING(yajl_tree_get(req->json, method_path,
201 yajl_t_string))) == NULL) {
202 jsonrpc_log(req, LOG_ERR,
203 "JSON-RPC method MUST exist and be a String");
204 return (JSONRPC_PARSE_ERROR);
205 }
206
207 /* Check params. */
208 req->params = yajl_tree_get(req->json, params_path, yajl_t_any);
209 if (!(req->params == NULL || YAJL_IS_ARRAY(req->params)
210 || YAJL_IS_OBJECT(req->params))) {
211 jsonrpc_log(req, LOG_ERR,
212 "JSON-RPC params MUST be Object or Array");
213 return (JSONRPC_PARSE_ERROR);
214 }
215
216 return (0);
217 }
218
219 int
220 jsonrpc_read_request(struct http_request *http_req, struct jsonrpc_request *req)
221 {
222 int ret;
223
224 init_request(req);
225 req->http = http_req;
226
227 if ((ret = read_json_body(http_req, req)) != 0)
228 return (ret);
229
230 return parse_json_body(req);
231 }
232
233 static int
234 write_id(yajl_gen gen, yajl_val id)
235 {
236 int status;
237
238 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(gen, "id")))
239 return (status);
240
241 if (YAJL_IS_NULL(id))
242 return yajl_gen_null(gen);
243
244 if (YAJL_IS_NUMBER(id)) {
245 if (YAJL_IS_INTEGER(id))
246 return yajl_gen_integer(gen, YAJL_GET_INTEGER(id));
247 return yajl_gen_null(gen);
248 }
249
250 if (YAJL_IS_STRING(id)) {
251 char *id_str = YAJL_GET_STRING(id);
252
253 return yajl_gen_string(gen, (unsigned char *)id_str,
254 strlen(id_str));
255 }
256
257 return yajl_gen_null(gen);
258 }
259
260 static int
261 open_response(yajl_gen genctx, yajl_val id)
262 {
263 int status;
264
265 if (YAJL_GEN_KO(status = yajl_gen_map_open(genctx)))
266 goto failed;
267 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(genctx, "jsonrpc")))
268 goto failed;
269 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(genctx, "2.0")))
270 goto failed;
271 status = write_id(genctx, id);
272 failed:
273 return (status);
274 }
275
276 static int
277 close_response(yajl_gen genctx)
278 {
279 int status;
280
281 if (YAJL_GEN_KO(status = yajl_gen_map_close(genctx)))
282 goto failed;
283 status = yajl_gen_map_close(genctx);
284 failed:
285 return (status);
286 }
287
288 static int
289 write_log(struct jsonrpc_request *req)
290 {
291 bool wrote_smth = false;
292 int status = 0;
293
294 for (struct jsonrpc_log *log = req->log.next; log != &req->log;
295 log = log->next) {
296
297 if (((1 << log->lvl) & req->log_levels) == 0)
298 continue;
299
300 if (!wrote_smth) {
301 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen,
302 "data")))
303 goto failed;
304 if (YAJL_GEN_KO(status = yajl_gen_array_open(req->gen)))
305 goto failed;
306 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 1);
307 wrote_smth = true;
308 }
309
310 if (YAJL_GEN_KO(status = yajl_gen_array_open(req->gen)))
311 goto failed;
312 if (YAJL_GEN_KO(status = yajl_gen_integer(req->gen, log->lvl)))
313 goto failed;
314 if (YAJL_GEN_KO(status = yajl_gen_string(req->gen,
315 (unsigned char *)log->msg, strlen(log->msg))))
316 goto failed;
317 if (YAJL_GEN_KO(status = yajl_gen_array_close(req->gen)))
318 goto failed;
319 }
320
321 if (wrote_smth) {
322 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0);
323 status = yajl_gen_array_close(req->gen);
324 }
325 failed:
326 return (status);
327 }
328
329 static int
330 write_error(struct jsonrpc_request *req, int code, const char *message)
331 {
332 int status;
333
334 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0);
335
336 if (YAJL_GEN_KO(status = open_response(req->gen, req->id)))
337 goto failed;
338 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "error")))
339 goto failed;
340 if (YAJL_GEN_KO(status = yajl_gen_map_open(req->gen)))
341 goto failed;
342 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "code")))
343 goto failed;
344 if (YAJL_GEN_KO(status = yajl_gen_integer(req->gen, code)))
345 goto failed;
346 if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "message")))
347 goto failed;
348
349 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 1);
350
351 if (YAJL_GEN_KO(status = yajl_gen_string(req->gen,
352 (const unsigned char *)message, strlen(message))))
353 goto failed;
354
355 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0);
356
357 if (YAJL_GEN_KO(status = write_log(req)))
358 goto failed;
359
360 status = close_response(req->gen);
361 failed:
362 return (status);
363 }
364
365 static const char *
366 known_msg(int code)
367 {
368 switch (code) {
369 case JSONRPC_PARSE_ERROR:
370 return (JSONRPC_PARSE_ERROR_MSG);
371 case JSONRPC_INVALID_REQUEST:
372 return (JSONRPC_INVALID_REQUEST_MSG);
373 case JSONRPC_METHOD_NOT_FOUND:
374 return (JSONRPC_METHOD_NOT_FOUND_MSG);
375 case JSONRPC_INVALID_PARAMS:
376 return (JSONRPC_INVALID_PARAMS_MSG);
377 case JSONRPC_INTERNAL_ERROR:
378 return (JSONRPC_INTERNAL_ERROR_MSG);
379 case JSONRPC_SERVER_ERROR:
380 return (JSONRPC_SERVER_ERROR_MSG);
381 case JSONRPC_LIMIT_REACHED:
382 return (JSONRPC_LIMIT_REACHED_MSG);
383 default:
384 return (NULL);
385 }
386 }
387
388 int
389 jsonrpc_error(struct jsonrpc_request *req, int code, const char *msg)
390 {
391 char *msg_fallback;
392 const unsigned char *body = NULL;
393 size_t body_len = 0;
394 int status;
395
396 if (req->id == NULL)
397 goto succeeded;
398
399 if ((req->gen = yajl_gen_alloc(NULL)) == NULL) {
400 kore_log(LOG_ERR, "jsonrpc_error: Failed to allocate yajl gen");
401 goto failed;
402 }
403
404 yajl_gen_config(req->gen, yajl_gen_beautify,
405 req->flags & yajl_gen_beautify);
406
407 if (msg == NULL)
408 msg = known_msg(code);
409
410 if (msg == NULL) {
411 size_t start = req->buf.offset;
412 kore_buf_appendf(&req->buf, "%d", code);
413 msg_fallback = kore_buf_stringify(&req->buf, NULL) + start;
414 }
415
416 if (YAJL_GEN_KO(status = write_error(req, code,
417 msg ? msg : msg_fallback))) {
418 kore_log(LOG_ERR, "jsonrpc_error: Failed to yajl gen text [%d]",
419 status);
420 goto failed;
421 }
422
423 http_response_header(req->http, "content-type", "application/json");
424 yajl_gen_get_buf(req->gen, &body, &body_len);
425 succeeded:
426 http_response(req->http, HTTP_STATUS_OK, body, body_len);
427 if (req->gen != NULL)
428 yajl_gen_clear(req->gen);
429 jsonrpc_destroy_request(req);
430 return (KORE_RESULT_OK);
431 failed:
432 http_response(req->http, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
433 jsonrpc_destroy_request(req);
434 return (KORE_RESULT_OK);
435 }
436
437 int
438 jsonrpc_result(struct jsonrpc_request *req,
439 int (*write_result)(struct jsonrpc_request *, void *), void *ctx)
440 {
441 const unsigned char *body = NULL;
442 size_t body_len = 0;
443
444 if (req->id == NULL)
445 goto succeeded;
446
447 if ((req->gen = yajl_gen_alloc(NULL)) == NULL) {
448 kore_log(LOG_ERR, "jsonrpc_result: Failed to allocate yajl gen");
449 goto failed;
450 }
451
452 yajl_gen_config(req->gen, yajl_gen_beautify,
453 req->flags & yajl_gen_beautify);
454
455 yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0);
456
457 if (YAJL_GEN_KO(open_response(req->gen, req->id)))
458 goto failed;
459 if (YAJL_GEN_KO(YAJL_GEN_CONST_STRING(req->gen, "result")))
460 goto failed;
461 if (YAJL_GEN_KO(write_result(req, ctx)))
462 goto failed;
463 if (YAJL_GEN_KO(yajl_gen_map_close(req->gen)))
464 goto failed;
465
466 http_response_header(req->http, "content-type", "application/json");
467 yajl_gen_get_buf(req->gen, &body, &body_len);
468 succeeded:
469 http_response(req->http, HTTP_STATUS_OK, body, body_len);
470 if (req->gen != NULL)
471 yajl_gen_clear(req->gen);
472 jsonrpc_destroy_request(req);
473 return (KORE_RESULT_OK);
474 failed:
475 http_response(req->http, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
476 jsonrpc_destroy_request(req);
477 return (KORE_RESULT_OK);
478 }