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
init_log(struct jsonrpc_log * log)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
append_log(struct jsonrpc_log * prev,int lvl,char * msg)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
free_log(struct jsonrpc_log * root)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
init_request(struct jsonrpc_request * req)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
jsonrpc_destroy_request(struct jsonrpc_request * req)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
jsonrpc_log(struct jsonrpc_request * req,int lvl,const char * fmt,...)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
read_json_body(struct http_request * http_req,struct jsonrpc_request * req)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
parse_json_body(struct jsonrpc_request * req)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
jsonrpc_read_request(struct http_request * http_req,struct jsonrpc_request * req)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
write_id(yajl_gen gen,yajl_val id)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
open_response(yajl_gen genctx,yajl_val id)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
close_response(yajl_gen genctx)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
write_log(struct jsonrpc_request * req)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
write_error(struct jsonrpc_request * req,int code,const char * message)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 *
known_msg(int code)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
jsonrpc_error(struct jsonrpc_request * req,int code,const char * msg)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, 200, 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, 500, NULL, 0);
433 	jsonrpc_destroy_request(req);
434 	return (KORE_RESULT_OK);
435 }
436 
437 int
jsonrpc_result(struct jsonrpc_request * req,int (* write_result)(struct jsonrpc_request *,void *),void * ctx)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, 200, 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, 500, NULL, 0);
476 	jsonrpc_destroy_request(req);
477 	return (KORE_RESULT_OK);
478 }
479