1 /* spoa-server: processing Lua
2  *
3  * Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version
8  * 2 of the License, or (at your option) any later version.
9  *
10  */
11 
12 #include <arpa/inet.h>
13 
14 #include <errno.h>
15 #include <string.h>
16 
17 #include <lauxlib.h>
18 #include <lua.h>
19 #include <lualib.h>
20 
21 #include "spoa.h"
22 
23 static lua_State *L = NULL;
24 static struct worker *worker;
25 
26 static int ps_lua_start_worker(struct worker *w);
27 static int ps_lua_load_file(struct worker *w, const char *file);
28 static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
29 
30 static struct ps ps_lua_bindings_1 = {
31 	.init_worker = ps_lua_start_worker,
32 	.load_file = ps_lua_load_file,
33 	.exec_message = ps_lua_exec_message,
34 	.ext = ".lua",
35 };
36 
37 static struct ps ps_lua_bindings_2 = {
38 	.init_worker = ps_lua_start_worker,
39 	.load_file = ps_lua_load_file,
40 	.exec_message = ps_lua_exec_message,
41 	.ext = ".luac",
42 };
43 
44 /* Imported from Lua-5.3.4 */
typeerror(lua_State * L,int arg,const char * tname)45 static int typeerror (lua_State *L, int arg, const char *tname)
46 {
47 	const char *msg;
48 	const char *typearg;  /* name for the type of the actual argument */
49 	if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
50 		typearg = lua_tostring(L, -1);  /* use the given type name */
51 	else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
52 		typearg = "light userdata";  /* special name for messages */
53 	else
54 		typearg = luaL_typename(L, arg);  /* standard name */
55 	msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
56 	return luaL_argerror(L, arg, msg);
57 }
58 
59 /* Imported from Lua-5.3.4 */
tag_error(lua_State * L,int arg,int tag)60 static void tag_error (lua_State *L, int arg, int tag) {
61 	typeerror(L, arg, lua_typename(L, tag));
62 }
63 
64 #ifndef luaL_checkboolean
luaL_checkboolean(lua_State * L,int index)65 static int luaL_checkboolean(lua_State *L, int index)
66 {
67 	if (!lua_isboolean(L, index)) {
68 		tag_error(L, index, LUA_TBOOLEAN);
69 	}
70 	return lua_toboolean(L, index);
71 }
72 #endif
73 
ps_lua_register_message(lua_State * L)74 static int ps_lua_register_message(lua_State *L)
75 {
76 	const char *name;
77 	long ref;
78 
79 	/* First argument is a message name */
80 	name = luaL_checkstring(L, 1);
81 
82 	/* Second argument is a function */
83 	if (!lua_isfunction(L, 2)) {
84 		const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, 2));
85 		luaL_argerror(L, 2, msg);
86 	}
87 	lua_pushvalue(L, 2);
88 	ref = luaL_ref(L, LUA_REGISTRYINDEX);
89 
90 	/* Register the message processor */
91 	ps_register_message(&ps_lua_bindings_1, name, (void *)ref);
92 
93 	return 1;
94 }
95 
ps_lua_set_var_null(lua_State * L)96 static int ps_lua_set_var_null(lua_State *L)
97 {
98 	const char *name;
99 	size_t name_len;
100 	unsigned char scope;
101 
102 	name = luaL_checklstring(L, 1, &name_len);
103 	scope = (unsigned char)luaL_checkinteger(L, 2);
104 
105 	if (!set_var_null(worker, name, name_len, scope)) {
106 		luaL_error(L, "No space left available");
107 	}
108 	return 0;
109 }
110 
ps_lua_set_var_boolean(lua_State * L)111 static int ps_lua_set_var_boolean(lua_State *L)
112 {
113 	const char *name;
114 	size_t name_len;
115 	unsigned char scope;
116 	int64_t value;
117 
118 	name = luaL_checklstring(L, 1, &name_len);
119 	scope = (unsigned char)luaL_checkinteger(L, 2);
120 	value = luaL_checkboolean(L, 3);
121 
122 	if (!set_var_bool(worker, name, name_len, scope, value))
123 		luaL_error(L, "No space left available");
124 	return 0;
125 }
126 
ps_lua_set_var_uint32(lua_State * L)127 static int ps_lua_set_var_uint32(lua_State *L)
128 {
129 	const char *name;
130 	size_t name_len;
131 	unsigned char scope;
132 	int64_t value;
133 
134 	name = luaL_checklstring(L, 1, &name_len);
135 	scope = (unsigned char)luaL_checkinteger(L, 2);
136 	value = luaL_checkinteger(L, 3);
137 
138 	if (value < 0 || value > UINT_MAX)
139 		luaL_error(L, "Integer '%lld' out of range for 'uint32' type", value);
140 
141 	if (!set_var_uint32(worker, name, name_len, scope, value))
142 		luaL_error(L, "No space left available");
143 	return 0;
144 }
145 
ps_lua_set_var_int32(lua_State * L)146 static int ps_lua_set_var_int32(lua_State *L)
147 {
148 	const char *name;
149 	size_t name_len;
150 	unsigned char scope;
151 	int64_t value;
152 
153 	name = luaL_checklstring(L, 1, &name_len);
154 	scope = (unsigned char)luaL_checkinteger(L, 2);
155 	value = luaL_checkinteger(L, 3);
156 
157 	if (value < INT_MIN || value > INT_MAX)
158 		luaL_error(L, "Integer '%lld' out of range for 'int32' type", value);
159 
160 	if (!set_var_int32(worker, name, name_len, scope, value))
161 		luaL_error(L, "No space left available");
162 	return 0;
163 }
164 
ps_lua_set_var_uint64(lua_State * L)165 static int ps_lua_set_var_uint64(lua_State *L)
166 {
167 	const char *name;
168 	size_t name_len;
169 	unsigned char scope;
170 	int64_t value;
171 
172 	name = luaL_checklstring(L, 1, &name_len);
173 	scope = (unsigned char)luaL_checkinteger(L, 2);
174 	value = luaL_checkinteger(L, 3);
175 
176 	if (value < 0)
177 		luaL_error(L, "Integer '%lld' out of range for 'uint64' type", value);
178 
179 	if (!set_var_uint64(worker, name, name_len, scope, value))
180 		luaL_error(L, "No space left available");
181 	return 0;
182 }
183 
ps_lua_set_var_int64(lua_State * L)184 static int ps_lua_set_var_int64(lua_State *L)
185 {
186 	const char *name;
187 	size_t name_len;
188 	unsigned char scope;
189 	int64_t value;
190 
191 	name = luaL_checklstring(L, 1, &name_len);
192 	scope = (unsigned char)luaL_checkinteger(L, 2);
193 	value = luaL_checkinteger(L, 3);
194 
195 	if (!set_var_int64(worker, name, name_len, scope, value))
196 		luaL_error(L, "No space left available");
197 	return 0;
198 }
199 
ps_lua_set_var_ipv4(lua_State * L)200 static int ps_lua_set_var_ipv4(lua_State *L)
201 {
202 	const char *name;
203 	size_t name_len;
204 	unsigned char scope;
205 	const char *value;
206 	struct in_addr ipv4;
207 	int ret;
208 
209 	name = luaL_checklstring(L, 1, &name_len);
210 	scope = (unsigned char)luaL_checkinteger(L, 2);
211 	value = luaL_checkstring(L, 3);
212 
213 	ret = inet_pton(AF_INET, value, &ipv4);
214 	if (ret == 0)
215 		luaL_error(L, "IPv4 '%s': invalid format", value);
216 	if (ret == -1)
217 		luaL_error(L, "IPv4 '%s': %s", value, strerror(errno));
218 
219 	if (!set_var_ipv4(worker, name, name_len, scope, &ipv4))
220 		luaL_error(L, "No space left available");
221 	return 0;
222 }
223 
ps_lua_set_var_ipv6(lua_State * L)224 static int ps_lua_set_var_ipv6(lua_State *L)
225 {
226 	const char *name;
227 	size_t name_len;
228 	unsigned char scope;
229 	const char *value;
230 	struct in6_addr ipv6;
231 	int ret;
232 
233 	name = luaL_checklstring(L, 1, &name_len);
234 	scope = (unsigned char)luaL_checkinteger(L, 2);
235 	value = luaL_checkstring(L, 3);
236 
237 	ret = inet_pton(AF_INET6, value, &ipv6);
238 	if (ret == 0)
239 		luaL_error(L, "IPv6 '%s': invalid format", value);
240 	if (ret == -1)
241 		luaL_error(L, "IPv6 '%s': %s", value, strerror(errno));
242 
243 	if (!set_var_ipv6(worker, name, name_len, scope, &ipv6))
244 		luaL_error(L, "No space left available");
245 	return 0;
246 }
247 
ps_lua_set_var_str(lua_State * L)248 static int ps_lua_set_var_str(lua_State *L)
249 {
250 	const char *name;
251 	size_t name_len;
252 	unsigned char scope;
253 	const char *value;
254 	size_t value_len;
255 
256 	name = luaL_checklstring(L, 1, &name_len);
257 	scope = (unsigned char)luaL_checkinteger(L, 2);
258 	value = luaL_checklstring(L, 3, &value_len);
259 
260 	if (!set_var_string(worker, name, name_len, scope, value, value_len))
261 		luaL_error(L, "No space left available");
262 	return 0;
263 }
264 
ps_lua_set_var_bin(lua_State * L)265 static int ps_lua_set_var_bin(lua_State *L)
266 {
267 	const char *name;
268 	size_t name_len;
269 	unsigned char scope;
270 	const char *value;
271 	size_t value_len;
272 
273 	name = luaL_checklstring(L, 1, &name_len);
274 	scope = (unsigned char)luaL_checkinteger(L, 2);
275 	value = luaL_checklstring(L, 3, &value_len);
276 
277 	if (!set_var_bin(worker, name, name_len, scope, value, value_len))
278 		luaL_error(L, "No space left available");
279 	return 0;
280 }
281 
ps_lua_start_worker(struct worker * w)282 static int ps_lua_start_worker(struct worker *w)
283 {
284 	if (L != NULL)
285 		return 1;
286 
287 	worker = w;
288 
289 	L = luaL_newstate();
290 	luaL_openlibs(L);
291 
292 	lua_newtable(L);
293 
294 	lua_pushstring(L, "register_message");
295 	lua_pushcclosure(L, ps_lua_register_message, 0);
296 	lua_rawset(L, -3);
297 
298 	lua_pushstring(L, "set_var_null");
299 	lua_pushcclosure(L, ps_lua_set_var_null, 0);
300 	lua_rawset(L, -3);
301 
302 	lua_pushstring(L, "set_var_boolean");
303 	lua_pushcclosure(L, ps_lua_set_var_boolean, 0);
304 	lua_rawset(L, -3);
305 
306 	lua_pushstring(L, "set_var_uint32");
307 	lua_pushcclosure(L, ps_lua_set_var_uint32, 0);
308 	lua_rawset(L, -3);
309 
310 	lua_pushstring(L, "set_var_int32");
311 	lua_pushcclosure(L, ps_lua_set_var_int32, 0);
312 	lua_rawset(L, -3);
313 
314 	lua_pushstring(L, "set_var_uint64");
315 	lua_pushcclosure(L, ps_lua_set_var_uint64, 0);
316 	lua_rawset(L, -3);
317 
318 	lua_pushstring(L, "set_var_int64");
319 	lua_pushcclosure(L, ps_lua_set_var_int64, 0);
320 	lua_rawset(L, -3);
321 
322 	lua_pushstring(L, "set_var_ipv4");
323 	lua_pushcclosure(L, ps_lua_set_var_ipv4, 0);
324 	lua_rawset(L, -3);
325 
326 	lua_pushstring(L, "set_var_ipv6");
327 	lua_pushcclosure(L, ps_lua_set_var_ipv6, 0);
328 	lua_rawset(L, -3);
329 
330 	lua_pushstring(L, "set_var_str");
331 	lua_pushcclosure(L, ps_lua_set_var_str, 0);
332 	lua_rawset(L, -3);
333 
334 	lua_pushstring(L, "set_var_bin");
335 	lua_pushcclosure(L, ps_lua_set_var_bin, 0);
336 	lua_rawset(L, -3);
337 
338 	lua_pushstring(L, "scope");
339 	lua_newtable(L);
340 
341 	lua_pushstring(L, "proc");
342 	lua_pushinteger(L, SPOE_SCOPE_PROC);
343 	lua_rawset(L, -3);
344 
345 	lua_pushstring(L, "sess");
346 	lua_pushinteger(L, SPOE_SCOPE_SESS);
347 	lua_rawset(L, -3);
348 
349 	lua_pushstring(L, "txn");
350 	lua_pushinteger(L, SPOE_SCOPE_TXN);
351 	lua_rawset(L, -3);
352 
353 	lua_pushstring(L, "req");
354 	lua_pushinteger(L, SPOE_SCOPE_REQ);
355 	lua_rawset(L, -3);
356 
357 	lua_pushstring(L, "res");
358 	lua_pushinteger(L, SPOE_SCOPE_RES);
359 	lua_rawset(L, -3);
360 
361 	lua_rawset(L, -3); /* scope */
362 
363 	lua_setglobal(L, "spoa");
364 	return 1;
365 }
366 
ps_lua_load_file(struct worker * w,const char * file)367 static int ps_lua_load_file(struct worker *w, const char *file)
368 {
369 	int error;
370 
371 	/* Load the file and check syntax */
372 	error = luaL_loadfile(L, file);
373 	if (error) {
374 		fprintf(stderr, "lua syntax error: %s\n", lua_tostring(L, -1));
375 		return 0;
376 	}
377 
378 	/* If no syntax error where detected, execute the code. */
379 	error = lua_pcall(L, 0, LUA_MULTRET, 0);
380    switch (error) {
381 	case LUA_OK:
382 		break;
383 	case LUA_ERRRUN:
384 		fprintf(stderr, "lua runtime error: %s\n", lua_tostring(L, -1));
385 		lua_pop(L, 1);
386 		return 0;
387 	case LUA_ERRMEM:
388 		fprintf(stderr, "lua out of memory error\n");
389 		return 0;
390 	case LUA_ERRERR:
391 		fprintf(stderr, "lua message handler error: %s\n", lua_tostring(L, 0));
392 		lua_pop(L, 1);
393 		return 0;
394 	case LUA_ERRGCMM:
395 		fprintf(stderr, "lua garbage collector error: %s\n", lua_tostring(L, 0));
396 		lua_pop(L, 1);
397 		return 0;
398 	default:
399 		fprintf(stderr, "lua unknown error: %s\n", lua_tostring(L, 0));
400 		lua_pop(L, 1);
401 		return 0;
402 	}
403 	return 1;
404 }
405 
ps_lua_exec_message(struct worker * w,void * ref,int nargs,struct spoe_kv * args)406 static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
407 {
408 	long lua_ref = (long)ref;
409 	int ret;
410 	char *msg_fmt = NULL;
411 	const char *msg;
412 	int i;
413 	char ipbuf[64];
414 
415 	/* Restore function in the stack */
416 	lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
417 
418 	/* convert args in lua mode */
419 	lua_newtable(L);
420 	for (i = 0; i < nargs; i++) {
421 		lua_newtable(L);
422 		lua_pushstring(L, "name");
423 		lua_pushlstring(L, args[i].name.str, args[i].name.len);
424 		lua_rawset(L, -3); /* Push name */
425 		lua_pushstring(L, "value");
426 		switch (args[i].value.type) {
427 		case SPOE_DATA_T_NULL:
428 			lua_pushnil(L);
429 			break;
430 		case SPOE_DATA_T_BOOL:
431 			lua_pushboolean(L, args[i].value.u.boolean);
432 			break;
433 		case SPOE_DATA_T_INT32:
434 			lua_pushinteger(L, args[i].value.u.sint32);
435 			break;
436 		case SPOE_DATA_T_UINT32:
437 			lua_pushinteger(L, args[i].value.u.uint32);
438 			break;
439 		case SPOE_DATA_T_INT64:
440 			lua_pushinteger(L, args[i].value.u.sint64);
441 			break;
442 		case SPOE_DATA_T_UINT64:
443 			if (args[i].value.u.uint64 > LLONG_MAX)
444 				lua_pushnil(L);
445 			else
446 				lua_pushinteger(L, args[i].value.u.uint64);
447 			break;
448 		case SPOE_DATA_T_IPV4:
449 			if (inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
450 				lua_pushnil(L);
451 			else
452 				lua_pushstring(L, ipbuf);
453 			break;
454 		case SPOE_DATA_T_IPV6:
455 			if (inet_ntop(AF_INET6, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
456 				lua_pushnil(L);
457 			else
458 				lua_pushstring(L, ipbuf);
459 			break;
460 		case SPOE_DATA_T_STR:
461 		case SPOE_DATA_T_BIN:
462 			lua_pushlstring(L, args[i].value.u.buffer.str, args[i].value.u.buffer.len);
463 			break;
464 		default:
465 			lua_pushnil(L);
466 			break;
467 		}
468 		lua_rawset(L, -3); /* Push name */
469 		lua_rawseti(L, -2, i + 1); /* Pusg table in globale table */
470 	}
471 
472 	/* execute lua function */
473 	while (1) {
474 		ret = lua_resume(L, L, 1);
475 		switch (ret) {
476 		case LUA_OK:
477 			return 1;
478 		case LUA_YIELD:
479 			DEBUG("Lua yield");
480 			continue;
481 		case LUA_ERRMEM:
482 			LOG("Lua: Out of memory error");
483 			return 0;
484 		case LUA_ERRRUN:
485 			msg_fmt = "Lua runtime error";
486 		case LUA_ERRGCMM:
487 			msg_fmt = msg_fmt ? msg_fmt : "Lua garbage collector error";
488 		case LUA_ERRERR:
489 			msg_fmt = msg_fmt ? msg_fmt : "Lua message handler error";
490 		default:
491 			msg_fmt = msg_fmt ? msg_fmt : "Lua unknown error";
492 			msg = lua_tostring(L, -1);
493 			if (msg == NULL)
494 				msg = "Unknown error";
495 			LOG("%s: %s", msg_fmt, msg);
496 			lua_settop(L, 0);
497 			return 0;
498 		}
499 	}
500 
501 	return 1;
502 }
503 
504 __attribute__((constructor))
__ps_lua_init(void)505 static void __ps_lua_init(void)
506 {
507 	ps_register(&ps_lua_bindings_1);
508 	ps_register(&ps_lua_bindings_2);
509 }
510