#include #include #include #include #ifdef __FreeBSD__ #include #elif defined(__linux__) #include #elif defined(__CYGWIN__) #include #endif #include #include "lua.h" #include "params.h" #include "helpers.h" #include "conntrack.h" #include "crypto/sha.h" #include "crypto/aes-gcm.h" #include "crypto/aes-ctr.h" void desync_instance(const char *func, unsigned int dp_n, unsigned int func_n, char *instance, size_t inst_size) { snprintf(instance, inst_size, "%s_%u_%u", func, dp_n, func_n); } static void lua_check_argc(lua_State *L, const char *where, int argc) { int num_args = lua_gettop(L); if (num_args != argc) luaL_error(L, "%s expect exactly %d arguments, got %d", where, argc, num_args); } static void lua_check_argc_range(lua_State *L, const char *where, int argc_min, int argc_max) { int num_args = lua_gettop(L); if (num_args < argc_min || num_args > argc_max) luaL_error(L, "%s expect from %d to %d arguments, got %d", where, argc_min, argc_max, num_args); } #if LUA_VERSION_NUM < 502 int lua_absindex(lua_State *L, int idx) { // convert relative index to absolute return idx<0 ? lua_gettop(params.L) + idx + 1 : idx; } #endif static int luacall_DLOG(lua_State *L) { lua_check_argc(L,"DLOG",1); DLOG("LUA: %s\n",luaL_checkstring(L,1)); return 0; } static int luacall_DLOG_ERR(lua_State *L) { lua_check_argc(L,"DLOG_ERR",1); DLOG_ERR("LUA: %s\n",luaL_checkstring(L,1)); return 0; } static int luacall_DLOG_CONDUP(lua_State *L) { lua_check_argc(L,"DLOG_CONDUP",1); DLOG_CONDUP("LUA: %s\n",luaL_checkstring(L,1)); return 0; } static int luacall_bitlshift(lua_State *L) { lua_check_argc(L,"bitlshift",2); int64_t v=(int64_t)luaL_checklint(L,1); if (v>0xFFFFFFFFFFFF || v<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); lua_pushlint(L,(((uint64_t)v) << luaL_checkinteger(L,2)) & 0xFFFFFFFFFFFF); return 1; } static int luacall_bitrshift(lua_State *L) { lua_check_argc(L,"bitrshift",2); int64_t v=(int64_t)luaL_checklint(L,1); if (v>0xFFFFFFFFFFFF || v<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); lua_pushlint(L,((uint64_t)v) >> luaL_checkinteger(L,2)); return 1; } static int luacall_bitand(lua_State *L) { lua_check_argc_range(L,"bitand",2,100); int argc = lua_gettop(L); int64_t v; uint64_t sum=0xFFFFFFFFFFFF; for(int i=1;i<=argc;i++) { v=(int64_t)luaL_checklint(L,i); if (v>0xFFFFFFFFFFFF || v<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); sum&=(uint64_t)v; } lua_pushlint(L,sum); return 1; } static int luacall_bitor(lua_State *L) { lua_check_argc_range(L,"bitor",1,100); int argc = lua_gettop(L); int64_t v; uint64_t sum=0; for(int i=1;i<=argc;i++) { v=(int64_t)luaL_checklint(L,i); if (v>0xFFFFFFFFFFFF || v<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); sum|=(uint64_t)v; } lua_pushlint(L,sum); return 1; } static int lua_bitnotx(lua_State *L, int64_t max) { lua_check_argc(L,"bitnot",1); int64_t v=(int64_t)luaL_checklint(L,1); if (v>max || v<-max) luaL_error(L, "out of range"); lua_pushlint(L,~(uint64_t)v & max); return 1; } static int luacall_bitnot8(lua_State *L) { lua_check_argc(L,"bitnot8",1); return lua_bitnotx(L, 0xFF); } static int luacall_bitnot16(lua_State *L) { lua_check_argc(L,"bitnot16",1); return lua_bitnotx(L, 0xFFFF); } static int luacall_bitnot24(lua_State *L) { lua_check_argc(L,"bitnot24",1); return lua_bitnotx(L, 0xFFFFFF); } static int luacall_bitnot32(lua_State *L) { lua_check_argc(L,"bitnot32",1); return lua_bitnotx(L, 0xFFFFFFFF); } static int luacall_bitnot48(lua_State *L) { lua_check_argc(L,"bitnot48",1); return lua_bitnotx(L, 0xFFFFFFFFFFFF); } static int luacall_bitxor(lua_State *L) { lua_check_argc_range(L,"bitxor",1,100); int argc = lua_gettop(L); int64_t v; uint64_t sum=0; for(int i=1;i<=argc;i++) { v=(int64_t)luaL_checklint(L,i); if (v>0xFFFFFFFFFFFF || v<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); sum^=(uint64_t)v; } lua_pushlint(L,sum); return 1; } static int luacall_bitget(lua_State *L) { lua_check_argc(L,"bitget",3); int64_t iwhat = (int64_t)luaL_checklint(L,1); if (iwhat>0xFFFFFFFFFFFF || iwhat<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); uint64_t what = (uint64_t)iwhat; lua_Integer from = luaL_checkinteger(L,2); lua_Integer to = luaL_checkinteger(L,3); if (from>to || from>47 || to>47) luaL_error(L, "bit range invalid"); what = (what >> from) & ~((lua_Integer)-1 << (to-from+1)); lua_pushlint(L,what); return 1; } static int luacall_bitset(lua_State *L) { lua_check_argc(L,"bitset",4); int64_t iwhat = (int64_t)luaL_checklint(L,1); if (iwhat>0xFFFFFFFFFFFF || iwhat<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); uint64_t what = (uint64_t)iwhat; lua_Integer from = luaL_checkinteger(L,2); lua_Integer to = luaL_checkinteger(L,3); int64_t iset = (int64_t)luaL_checklint(L,4); if (iset>0xFFFFFFFFFFFF || iset<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); uint64_t set = (uint64_t)iset; if (from>to || from>47 || to>47) luaL_error(L, "bit range invalid"); lua_Integer mask = ~((lua_Integer)-1 << (to-from+1)); set = (set & mask) << from; mask <<= from; what = what & ~mask | set; lua_pushlint(L,what); return 1; } static int luacall_u8(lua_State *L) { lua_check_argc_range(L,"u8",1,2); int argc=lua_gettop(L); size_t l; lua_Integer offset; const uint8_t *p = (uint8_t*)luaL_checklstring(L,1,&l); offset = (argc>=2 && lua_type(L,2)!=LUA_TNIL) ? luaL_checkinteger(L,2)-1 : 0; if (offset<0 || (offset+1)>l) luaL_error(L, "out of range"); lua_pushinteger(L,p[offset]); return 1; } static int luacall_u16(lua_State *L) { lua_check_argc_range(L,"u16",1,2); int argc=lua_gettop(L); size_t l; lua_Integer offset; const uint8_t *p = (uint8_t*)luaL_checklstring(L,1,&l); offset = (argc>=2 && lua_type(L,2)!=LUA_TNIL) ? luaL_checkinteger(L,2)-1 : 0; if (offset<0 || (offset+2)>l) luaL_error(L, "out of range"); lua_pushinteger(L,pntoh16(p+offset)); return 1; } static int luacall_u24(lua_State *L) { lua_check_argc_range(L,"u24",1,2); int argc=lua_gettop(L); size_t l; lua_Integer offset; const uint8_t *p = (uint8_t*)luaL_checklstring(L,1,&l); offset = (argc>=2 && lua_type(L,2)!=LUA_TNIL) ? luaL_checkinteger(L,2)-1 : 0; if (offset<0 || (offset+3)>l) luaL_error(L, "out of range"); lua_pushinteger(L,pntoh24(p+offset)); return 1; } static int luacall_u32(lua_State *L) { lua_check_argc_range(L,"u32",1,2); int argc=lua_gettop(L); size_t l; lua_Integer offset; const uint8_t *p = (uint8_t*)luaL_checklstring(L,1,&l); offset = (argc>=2 && lua_type(L,2)!=LUA_TNIL) ? luaL_checkinteger(L,2)-1 : 0; if (offset<0 || (offset+4)>l) luaL_error(L, "out of range"); lua_pushlint(L,pntoh32(p+offset)); return 1; } static int luacall_u48(lua_State *L) { lua_check_argc_range(L,"u48",1,2); int argc=lua_gettop(L); size_t l; lua_Integer offset; const uint8_t *p = (uint8_t*)luaL_checklstring(L,1,&l); offset = (argc>=2 && lua_type(L,2)!=LUA_TNIL) ? luaL_checkinteger(L,2)-1 : 0; if (offset<0 || (offset+6)>l) luaL_error(L, "out of range"); lua_pushlint(L,pntoh48(p+offset)); return 1; } static int luacall_swap16(lua_State *L) { lua_check_argc(L,"swap16",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFFFF || i<-(int64_t)0xFFFF) luaL_error(L, "out of range"); uint16_t u = (uint16_t)i; lua_pushinteger(L,swap16(u)); return 1; } static int luacall_swap24(lua_State *L) { lua_check_argc(L,"swap24",1); int64_t i =(int64_t)luaL_checklint(L,1); if (i>0xFFFFFF || i<-(int64_t)0xFFFFFF) luaL_error(L, "out of range"); uint32_t u = (uint32_t)i; lua_pushlint(L,swap24(u)); return 1; } static int luacall_swap32(lua_State *L) { lua_check_argc(L,"swap32",1); int64_t i =(int64_t)luaL_checklint(L,1); if (i>0xFFFFFFFF || i<-(int64_t)0xFFFFFFFF) luaL_error(L, "out of range"); uint32_t u = (uint32_t)i; lua_pushlint(L,__builtin_bswap32(u)); return 1; } static int luacall_swap48(lua_State *L) { lua_check_argc(L,"swap48",1); int64_t i =(int64_t)luaL_checklint(L,1); if (i>0xFFFFFFFFFFFF || i<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); uint64_t u = (uint64_t)i; lua_pushlint(L, swap48(u)); return 1; } static int lua_uxadd(lua_State *L, int64_t max) { int64_t v; uint64_t sum=0; int argc = lua_gettop(L); for(int i=1;i<=argc;i++) { v = (int64_t)luaL_checklint(L,i); if (v>max || v<-max) luaL_error(L, "out of range"); sum+=(uint64_t)v; } lua_pushlint(L, sum & max); return 1; } static int luacall_u8add(lua_State *L) { lua_check_argc_range(L,"u8add",1,100); return lua_uxadd(L, 0xFF); } static int luacall_u16add(lua_State *L) { lua_check_argc_range(L,"u16add",1,100); return lua_uxadd(L, 0xFFFF); } static int luacall_u24add(lua_State *L) { lua_check_argc_range(L,"u24add",1,100); return lua_uxadd(L, 0xFFFFFF); } static int luacall_u32add(lua_State *L) { lua_check_argc_range(L,"u32add",1,100); return lua_uxadd(L, 0xFFFFFFFF); } static int luacall_u48add(lua_State *L) { lua_check_argc_range(L,"u48add",1,100); return lua_uxadd(L, 0xFFFFFFFFFFFF); } static int luacall_bu8(lua_State *L) { lua_check_argc(L,"bu8",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFF || i<-(lua_Integer)0xFF) luaL_error(L, "out of range"); uint8_t v=(uint8_t)i; lua_pushlstring(L,(char*)&v,1); return 1; } static int luacall_bu16(lua_State *L) { lua_check_argc(L,"bu16",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFFFF || i<-(lua_Integer)0xFFFF) luaL_error(L, "out of range"); uint8_t v[2]; phton16(v,(uint16_t)i); lua_pushlstring(L,(char*)v,2); return 1; } static int luacall_bu24(lua_State *L) { lua_check_argc(L,"bu24",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFFFFFF || i<-(lua_Integer)0xFFFFFF) luaL_error(L, "out of range"); uint8_t v[3]; phton24(v,(uint32_t)i); lua_pushlstring(L,(char*)v,3); return 1; } static int luacall_bu32(lua_State *L) { lua_check_argc(L,"bu32",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFFFFFFFF || i<-(int64_t)0xFFFFFFFF) luaL_error(L, "out of range"); uint8_t v[4]; phton32(v,(uint32_t)i); lua_pushlstring(L,(char*)v,4); return 1; } static int luacall_bu48(lua_State *L) { lua_check_argc(L,"bu48",1); int64_t i = (int64_t)luaL_checklint(L,1); if (i>0xFFFFFFFFFFFF || i<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range"); uint8_t v[4]; phton48(v,(uint64_t)i); lua_pushlstring(L,(char*)v,6); return 1; } static int luacall_divint(lua_State *L) { lua_check_argc(L,"divint",2); int64_t v1=(int64_t)luaL_checklint(L,1); int64_t v2=(int64_t)luaL_checklint(L,2); if (v2) lua_pushlint(L,v1/v2); else lua_pushnil(L); return 1; } static int luacall_brandom(lua_State *L) { lua_check_argc(L,"brandom",1); lua_Integer len = luaL_checkinteger(L,1); uint8_t *p = malloc(len); if (!p) luaL_error(L, "out of memory"); fill_random_bytes(p,len); // in out of memory condition this will leave p unfreed lua_pushlstring(L,(char*)p,len); free(p); return 1; } static int luacall_brandom_az(lua_State *L) { lua_check_argc(L,"brandom_az",1); lua_Integer len = luaL_checkinteger(L,1); uint8_t *p = malloc(len); if (!p) luaL_error(L, "out of memory"); fill_random_az(p,len); // in out of memory condition this will leave p unfreed lua_pushlstring(L,(char*)p,len); free(p); return 1; } static int luacall_brandom_az09(lua_State *L) { lua_check_argc(L,"brandom_az09",1); lua_Integer len = luaL_checkinteger(L,1); uint8_t *p = malloc(len); if (!p) luaL_error(L, "out of memory"); fill_random_az09(p,len); // in out of memory condition this will leave p unfreed lua_pushlstring(L,(char*)p,len); free(p); return 1; } // hacky function. breaks immutable string behavior. // if you change a string, it will change in all variables that hold the same string static int luacall_memcpy(lua_State *L) { // memcpy(to,to_offset,from,from_offset,size) lua_check_argc_range(L,"memcpy",3,5); size_t lfrom,lto; lua_Integer off_from,off_to,size; int argc=lua_gettop(L); const uint8_t *from = (uint8_t*)luaL_checklstring(L,3,&lfrom); uint8_t *to = (uint8_t*)luaL_checklstring(L,1,<o); off_from = argc>=4 ? luaL_checkinteger(L,4)-1 : 0; off_to = luaL_checkinteger(L,2)-1; if (off_from<0 || off_to<0 || off_from>lfrom || off_to>lto) luaL_error(L, "out of range"); size = argc>=5 ? luaL_checkinteger(L,5) : lfrom-off_from; if (size<0 || (off_from+size)>lfrom || (off_to+size)>lto) luaL_error(L, "out of range"); memcpy(to+off_to,from+off_from,size); return 0; } static int luacall_parse_hex(lua_State *L) { lua_check_argc(L,"parse_hex",1); LUA_STACK_GUARD_ENTER(L) size_t l; const char *hex = luaL_checklstring(L,1,&l); if ((l&1)) goto err; l>>=1; uint8_t *p = malloc(l); if (!p) goto err; if (!parse_hex_str(hex,p,&l)) { free(p); goto err; } // in out of memory condition this will leave p unfreed lua_pushlstring(L,(char*)p,l); free(p); ex: LUA_STACK_GUARD_RETURN(L,1) err: lua_pushnil(L); goto ex; } static SHAversion lua_hash_type(const char *s_hash_type) { SHAversion sha_ver; if (!strcmp(s_hash_type,"sha256")) sha_ver = SHA256; else if (!strcmp(s_hash_type,"sha224")) sha_ver = SHA224; else luaL_error(params.L, "unsupported hash type %s", s_hash_type); return sha_ver; } static int luacall_bcryptorandom(lua_State *L) { lua_check_argc(L,"bcryptorandom",1); LUA_STACK_GUARD_ENTER(L) lua_Integer len = luaL_checkinteger(L,1); uint8_t *p = malloc(len); if (!p) luaL_error(L, "out of memory"); if (!fill_crypto_random_bytes(p,len)) { free(p); // this is fatal. they expect us to give them crypto secure random blob luaL_error(L, "could not read random data from /dev/random"); } lua_pushlstring(L,(char*)p,len); free(p); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_hash(lua_State *L) { // hash(hash_type, data) returns hash lua_check_argc(L,"hash",2); LUA_STACK_GUARD_ENTER(L) const char *s_hash_type = luaL_checkstring(L,1); SHAversion sha_ver = lua_hash_type(s_hash_type); size_t data_len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,2,&data_len); unsigned char hash[USHAMaxHashSize]; USHAContext tcontext; if (USHAReset(&tcontext, sha_ver)!=shaSuccess || USHAInput(&tcontext, data, data_len)!=shaSuccess || USHAResult(&tcontext, hash)!=shaSuccess) luaL_error(L, "hash failure"); lua_pushlstring(L,(char*)hash,USHAHashSize(sha_ver)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_aes(lua_State *L) { // aes_gcm(bEncrypt, key, in) returns out lua_check_argc(L,"aes",3); LUA_STACK_GUARD_ENTER(L) bool bEncrypt = lua_toboolean(L,1); size_t key_len; const uint8_t *key = (uint8_t*)luaL_checklstring(L,2,&key_len); if (key_len!=16 && key_len!=24 && key_len!=32) luaL_error(L, "aes: wrong key length %u. should be 16,24,32.", (unsigned)key_len); size_t input_len; const uint8_t *input = (uint8_t*)luaL_checklstring(L,3,&input_len); if (input_len!=16) luaL_error(L, "aes: wrong data length %u. should be 16.", (unsigned)input_len); aes_context ctx; uint8_t output[16]; if (aes_setkey(&ctx, bEncrypt, key, key_len) || aes_cipher(&ctx, input, output)) lua_pushnil(L); else lua_pushlstring(L,(const char*)output,sizeof(output)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_aes_gcm(lua_State *L) { // aes_gcm(bEncrypt, key, iv, in, [additional_data]) returns out, atag lua_check_argc_range(L,"aes_gcm",4,5); LUA_STACK_GUARD_ENTER(L) int argc = lua_gettop(L); bool bEncrypt = lua_toboolean(L,1); size_t key_len; const uint8_t *key = (uint8_t*)luaL_checklstring(L,2,&key_len); if (key_len!=16 && key_len!=24 && key_len!=32) luaL_error(L, "aes_gcm: wrong key length %u. should be 16,24,32.", (unsigned)key_len); size_t iv_len; const uint8_t *iv = (uint8_t*)luaL_checklstring(L,3,&iv_len); if (iv_len!=12) luaL_error(L, "aes_gcm: wrong iv length %u. should be 12.", (unsigned)iv_len); size_t input_len; const uint8_t *input = (uint8_t*)luaL_checklstring(L,4,&input_len); size_t add_len=0; const uint8_t *add = lua_isnoneornil(L,5) ? NULL : (uint8_t*)luaL_checklstring(L,5,&add_len); uint8_t atag[16]; uint8_t *output = malloc(input_len); if (!output) luaL_error(L, "out of memory"); if (aes_gcm_crypt(bEncrypt, output, input, input_len, key, key_len, iv, iv_len, add, add_len, atag, sizeof(atag))) { lua_pushnil(L); lua_pushnil(L); } else { lua_pushlstring(L,(const char*)output,input_len); lua_pushlstring(L,(const char*)atag,sizeof(atag)); } free(output); LUA_STACK_GUARD_RETURN(L,2) } static int luacall_aes_ctr(lua_State *L) { // aes_ctr(key, iv, in) returns out lua_check_argc(L,"aes_ctr",3); LUA_STACK_GUARD_ENTER(L) size_t key_len; const uint8_t *key = (uint8_t*)luaL_checklstring(L,1,&key_len); if (key_len!=16 && key_len!=24 && key_len!=32) luaL_error(L, "aes_ctr: wrong key length %u. should be 16,24,32.", (unsigned)key_len); size_t iv_len; const uint8_t *iv = (uint8_t*)luaL_checklstring(L,2,&iv_len); if (iv_len!=16) luaL_error(L, "aes_ctr: wrong iv length %u. should be 16.", (unsigned)iv_len); size_t input_len; const uint8_t *input = (uint8_t*)luaL_checklstring(L,3,&input_len); uint8_t *output = malloc(input_len); if (!output) luaL_error(L, "out of memory"); if (aes_ctr_crypt(key, key_len, iv, input, input_len, output)) lua_pushnil(L); else lua_pushlstring(L,(const char*)output,input_len); free(output); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_hkdf(lua_State *L) { // hkdf(hash_type, salt, ikm, info, okm_len) returns okm // hash_type - string "sha224" or "sha256" lua_check_argc(L,"hkdf",5); LUA_STACK_GUARD_ENTER(L) const char *s_hash_type = luaL_checkstring(L,1); SHAversion sha_ver = lua_hash_type(s_hash_type); size_t salt_len=0; const uint8_t *salt = lua_type(L,2) == LUA_TNIL ? NULL : (uint8_t*)luaL_checklstring(L,2,&salt_len); size_t ikm_len=0; const uint8_t *ikm = lua_type(L,3) == LUA_TNIL ? NULL : (uint8_t*)luaL_checklstring(L,3,&ikm_len); size_t info_len=0; const uint8_t *info = lua_type(L,4) == LUA_TNIL ? NULL : (uint8_t*)luaL_checklstring(L,4,&info_len); size_t okm_len = (size_t)luaL_checkinteger(L,5); uint8_t *okm = malloc(okm_len); if (!okm) luaL_error(L, "out of memory"); if (hkdf(sha_ver, salt, salt_len, ikm, ikm_len, info, info_len, okm, okm_len)) lua_pushnil(L); else lua_pushlstring(L,(const char*)okm, okm_len); free(okm); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_getpid(lua_State *L) { lua_check_argc(L,"getpid", 0); lua_pushinteger(L, getpid()); return 1; } static int luacall_gettid(lua_State *L) { lua_check_argc(L,"gettid", 0); #ifdef __OpenBSD__ lua_pushinteger(L, getthrid()); #elif defined(__FreeBSD__) long tid; if (thr_self(&tid)) lua_pushnil(L); else lua_pushinteger(L, tid); #elif defined(__linux__) lua_pushinteger(L, syscall(SYS_gettid)); #elif defined(__CYGWIN__) lua_pushinteger(L, GetCurrentThreadId()); #else // unsupported OS ? lua_pushnil(L); #endif return 1; } static int luacall_uname(lua_State *L) { lua_check_argc(L,"uname", 0); LUA_STACK_GUARD_ENTER(L) struct utsname udata; if (uname(&udata)) lua_pushnil(L); else { lua_createtable(params.L, 0, 5); lua_pushf_str("sysname", udata.sysname); lua_pushf_str("nodename", udata.nodename); lua_pushf_str("release", udata.release); lua_pushf_str("version", udata.version); lua_pushf_str("machine", udata.machine); } LUA_STACK_GUARD_RETURN(L,1) } static int luacall_clock_gettime(lua_State *L) { lua_check_argc(L,"clock_gettime", 0); LUA_STACK_GUARD_ENTER(L) struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts)) { lua_pushnil(L); lua_pushnil(L); } else { lua_pushlint(L, ts.tv_sec); lua_pushinteger(L, ts.tv_nsec); } LUA_STACK_GUARD_RETURN(L,2) } static t_lua_desync_context *lua_desync_ctx() { if (lua_isnil(params.L,1)) luaL_error(params.L, "missing ctx"); if (!lua_islightuserdata(params.L,1)) luaL_error(params.L, "bad ctx - invalid data type"); t_lua_desync_context *ctx = lua_touserdata(params.L,1); // ensure it's really ctx. LUA could pass us any lightuserdata pointer if (ctx->magic!=MAGIC_CTX) luaL_error(params.L, "bad ctx - magic bytes invalid"); return ctx; } static int luacall_instance_cutoff(lua_State *L) { // out : instance_name.profile_number[0] // in : instance_name.profile_number[1] lua_check_argc_range(L,"instance_cutoff",1,2); LUA_STACK_GUARD_ENTER(L) if (lua_isnil(L,1)) // this can happen in orchestrated function. they do not have their own ctx and they cant cutoff DLOG("instance cutoff not possible because missing ctx\n"); else { const t_lua_desync_context *ctx = lua_desync_ctx(); int argc=lua_gettop(L); bool bIn,bOut; if (argc>=2 && lua_type(L,2)!=LUA_TNIL) { luaL_checktype(L,2,LUA_TBOOLEAN); bOut = lua_toboolean(L,2); bIn = !bOut; } else bIn = bOut = true; if (ctx->ctrack) { DLOG("instance cutoff for '%s' in=%u out=%u\n",ctx->instance,bIn,bOut); lua_rawgeti(L,LUA_REGISTRYINDEX,ctx->ctrack->lua_instance_cutoff); lua_getfield(L,-1,ctx->instance); if (!lua_istable(L,-1)) { lua_pop(L,1); lua_pushf_table(ctx->instance); lua_getfield(L,-1,ctx->instance); } lua_rawgeti(L,-1,ctx->dp->n); if (!lua_istable(L,-1)) { lua_pop(L,1); lua_pushi_table(ctx->dp->n); lua_rawgeti(L,-1,ctx->dp->n); } if (bOut) lua_pushi_bool(0,true); if (bIn) lua_pushi_bool(1,true); lua_pop(L,3); } else DLOG("instance cutoff requested for '%s' in=%u out=%u but not possible without conntrack\n",ctx->instance,bIn,bOut); } LUA_STACK_GUARD_RETURN(L,0) } bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn) { bool b=false; // out : func_name.profile_number[0] // in : func_name.profile_number[1] if (ctx->ctrack) { lua_rawgeti(params.L,LUA_REGISTRYINDEX,ctx->ctrack->lua_instance_cutoff); lua_getfield(params.L,-1,ctx->instance); if (!lua_istable(params.L,-1)) { lua_pop(params.L,2); return false; } lua_rawgeti(params.L,-1,ctx->dp->n); if (!lua_istable(params.L,-1)) { lua_pop(params.L,3); return false; } lua_rawgeti(params.L,-1,bIn); b = lua_toboolean(params.L,-1); lua_pop(params.L,4); } return b; } static int luacall_lua_cutoff(lua_State *L) { lua_check_argc_range(L,"lua_cutoff",1,2); LUA_STACK_GUARD_ENTER(L) t_lua_desync_context *ctx = lua_desync_ctx(); int argc=lua_gettop(L); bool bIn,bOut; if (argc>=2 && lua_type(L,2)!=LUA_TNIL) { luaL_checktype(L,2,LUA_TBOOLEAN); bOut = lua_toboolean(L,2); bIn = !bOut; } else bIn = bOut = true; if (ctx->ctrack) { DLOG("lua cutoff from '%s' in=%u out=%u\n",ctx->instance,bIn,bOut); // lua cutoff is one way transition if (bIn) ctx->ctrack->b_lua_in_cutoff = true; if (bOut) ctx->ctrack->b_lua_out_cutoff = true; } else DLOG("lua cutoff requested from '%s' in=%u out=%u but not possible without conntrack\n",ctx->instance,bIn,bOut); LUA_STACK_GUARD_RETURN(L,0) } static int luacall_execution_plan(lua_State *L) { lua_check_argc(L,"execution_plan",1); LUA_STACK_GUARD_ENTER(L) t_lua_desync_context *ctx = lua_desync_ctx(); lua_newtable(L); struct func_list *func; char instance[256], pls[2048]; struct packet_range *range; unsigned int n=1; LIST_FOREACH(func, &ctx->dp->lua_desync, next) { if (n > ctx->func_n) { desync_instance(func->func, ctx->dp->n, n, instance, sizeof(instance)); range = ctx->incoming ? &func->range_in : &func->range_out; lua_pushinteger(params.L, n - ctx->func_n); lua_createtable(params.L, 0, 6); lua_pushf_args(&func->args, -1, false); lua_pushf_str("func", func->func); lua_pushf_int("func_n", ctx->func_n); lua_pushf_str("func_instance", instance); lua_pushf_range("range", range); if (l7_payload_str_list(func->payload_type, pls, sizeof(pls))) lua_pushf_str("payload_filter", pls); else lua_pushf_nil("payload_filter"); lua_rawset(params.L,-3); } n++; } LUA_STACK_GUARD_RETURN(L,1) } static int luacall_execution_plan_cancel(lua_State *L) { lua_check_argc(L,"execution_plan_cancel",1); t_lua_desync_context *ctx = lua_desync_ctx(); DLOG("execution plan cancel from '%s'\n",ctx->instance); ctx->cancel = true; return 0; } static int luacall_raw_packet(lua_State *L) { lua_check_argc(L,"raw_packet",1); LUA_STACK_GUARD_ENTER(L) const t_lua_desync_context *ctx = lua_desync_ctx(); lua_pushlstring(L, (const char*)ctx->dis->data_pkt, ctx->dis->len_pkt); LUA_STACK_GUARD_RETURN(L,1) } void lua_pushf_nil(const char *field) { lua_pushstring(params.L, field); lua_pushnil(params.L); lua_rawset(params.L,-3); } void lua_pushi_nil(lua_Integer idx) { lua_pushinteger(params.L, idx); lua_pushnil(params.L); lua_rawset(params.L,-3); } void lua_pushf_int(const char *field, lua_Integer v) { lua_pushstring(params.L, field); lua_pushlint(params.L, v); lua_rawset(params.L,-3); } void lua_pushi_int(lua_Integer idx, lua_Integer v) { lua_pushinteger(params.L, idx); lua_pushlint(params.L, v); lua_rawset(params.L,-3); } void lua_pushf_lint(const char *field, int64_t v) { lua_pushstring(params.L, field); lua_pushlint(params.L, v); lua_rawset(params.L,-3); } void lua_pushi_lint(lua_Integer idx, int64_t v) { lua_pushinteger(params.L, idx); lua_pushlint(params.L, v); lua_rawset(params.L,-3); } void lua_pushf_number(const char *field, lua_Number v) { lua_pushstring(params.L, field); lua_pushnumber(params.L, v); lua_rawset(params.L,-3); } void lua_pushi_number(lua_Integer idx, lua_Number v) { lua_pushinteger(params.L, idx); lua_pushnumber(params.L, v); lua_rawset(params.L,-3); } void lua_pushf_bool(const char *field, bool b) { lua_pushstring(params.L, field); lua_pushboolean(params.L, b); lua_rawset(params.L,-3); } void lua_pushi_bool(lua_Integer idx, bool b) { lua_pushinteger(params.L, idx); lua_pushboolean(params.L, b); lua_rawset(params.L,-3); } void lua_pushf_str(const char *field, const char *str) { lua_pushstring(params.L, field); lua_pushstring(params.L, str); // pushes nil if str==NULL lua_rawset(params.L,-3); } void lua_pushi_str(lua_Integer idx, const char *str) { lua_pushinteger(params.L, idx); lua_pushstring(params.L, str); // pushes nil if str==NULL lua_rawset(params.L,-3); } void lua_pushf_lstr(const char *field, const char *str, size_t size) { lua_pushstring(params.L, field); lua_pushlstring(params.L, str, size); lua_rawset(params.L,-3); } void lua_pushi_lstr(lua_Integer idx, const char *str, size_t size) { lua_pushinteger(params.L, idx); lua_pushlstring(params.L, str, size); lua_rawset(params.L,-3); } void lua_push_raw(const void *v, size_t l) { if (v) lua_pushlstring(params.L, (char*)v, l); else lua_pushnil(params.L); } void lua_pushf_raw(const char *field, const void *v, size_t l) { lua_pushstring(params.L, field); lua_push_raw(v,l); lua_rawset(params.L,-3); } void lua_pushi_raw(lua_Integer idx, const void *v, size_t l) { lua_pushinteger(params.L, idx); lua_push_raw(v,l); lua_rawset(params.L,-3); } void lua_pushf_reg(const char *field, int ref) { lua_pushstring(params.L, field); lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref); lua_rawset(params.L, -3); } void lua_pushf_lud(const char *field, void *p) { lua_pushstring(params.L, field); lua_pushlightuserdata(params.L, p); lua_rawset(params.L,-3); } void lua_pushf_table(const char *field) { lua_pushstring(params.L, field); lua_newtable(params.L); lua_rawset(params.L,-3); } void lua_pushi_table(lua_Integer idx) { lua_pushinteger(params.L, idx); lua_newtable(params.L); lua_rawset(params.L,-3); } void lua_pushf_global(const char *field, const char *global) { lua_pushstring(params.L, field); lua_getglobal(params.L, global); lua_rawset(params.L,-3); } void lua_push_blob(int idx_desync, const char *blob) { lua_getfield(params.L, idx_desync, blob); if (lua_type(params.L,-1)==LUA_TNIL) { lua_pop(params.L,1); lua_getglobal(params.L, blob); } } void lua_pushf_blob(int idx_desync, const char *field, const char *blob) { lua_pushstring(params.L, field); lua_push_blob(idx_desync, blob); lua_rawset(params.L,-3); } void lua_pushf_tcphdr_options(const struct tcphdr *tcp, size_t len) { LUA_STACK_GUARD_ENTER(params.L) lua_pushliteral(params.L,"options"); lua_newtable(params.L); uint8_t *t = (uint8_t*)(tcp+1); uint8_t *end = (uint8_t*)tcp + (tcp->th_off<<2); uint8_t opt; if ((end-(uint8_t*)tcp) < len) end=(uint8_t*)tcp + len; lua_Integer idx=1; while(t=end || t[1]<2 || (t+t[1])>end) break; lua_pushinteger(params.L,idx); lua_newtable(params.L); lua_pushf_int("kind",opt); lua_pushf_raw("data",t+2,t[1]-2); t+=t[1]; } lua_rawset(params.L,-3); if (opt==TCP_KIND_END) break; idx++; } lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_tcphdr(const struct tcphdr *tcp, size_t len) { LUA_STACK_GUARD_ENTER(params.L) lua_pushliteral(params.L, "tcp"); if (tcp && len>=sizeof(struct tcphdr)) { lua_createtable(params.L, 0, 11); lua_pushf_int("th_sport",ntohs(tcp->th_sport)); lua_pushf_int("th_dport",ntohs(tcp->th_dport)); lua_pushf_lint("th_seq",ntohl(tcp->th_seq)); lua_pushf_lint("th_ack",ntohl(tcp->th_ack)); lua_pushf_int("th_x2",tcp->th_x2); lua_pushf_int("th_off",tcp->th_off); lua_pushf_int("th_flags",tcp->th_flags); lua_pushf_int("th_win",ntohs(tcp->th_win)); lua_pushf_int("th_sum",ntohs(tcp->th_sum)); lua_pushf_int("th_urp",ntohs(tcp->th_urp)); lua_pushf_tcphdr_options(tcp,len); } else lua_pushnil(params.L); lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_udphdr(const struct udphdr *udp, size_t len) { LUA_STACK_GUARD_ENTER(params.L) lua_pushliteral(params.L, "udp"); if (udp && len>=sizeof(struct udphdr)) { lua_createtable(params.L, 0, 4); lua_pushf_int("uh_sport",ntohs(udp->uh_sport)); lua_pushf_int("uh_dport",ntohs(udp->uh_dport)); lua_pushf_int("uh_ulen",ntohs(udp->uh_ulen)); lua_pushf_int("uh_sum",ntohs(udp->uh_sum)); } else lua_pushnil(params.L); lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_iphdr(const struct ip *ip, size_t len) { LUA_STACK_GUARD_ENTER(params.L) lua_pushliteral(params.L, "ip"); if (ip && len>=sizeof(struct ip)) { uint16_t hl = ip->ip_hl<<2; bool b_has_opt = hl>sizeof(struct tcphdr) && hl<=len; lua_createtable(params.L, 0, 11+b_has_opt); lua_pushf_int("ip_v",ip->ip_v); lua_pushf_int("ip_hl",ip->ip_hl); lua_pushf_int("ip_tos",ip->ip_tos); lua_pushf_int("ip_len",ntohs(ip->ip_len)); lua_pushf_int("ip_id",ntohs(ip->ip_id)); lua_pushf_int("ip_off",ntohs(ip->ip_off)); lua_pushf_int("ip_ttl",ip->ip_ttl); lua_pushf_int("ip_p",ip->ip_p); lua_pushf_int("ip_sum",ip->ip_sum); lua_pushf_raw("ip_src",&ip->ip_src,sizeof(struct in_addr)); lua_pushf_raw("ip_dst",&ip->ip_dst,sizeof(struct in_addr)); if (b_has_opt) lua_pushf_raw("options",(uint8_t*)(ip+1),hl-sizeof(struct tcphdr)); } else lua_pushnil(params.L); lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_ip6exthdr(const struct ip6_hdr *ip6, size_t len) { LUA_STACK_GUARD_ENTER(params.L); // assume ipv6 packet structure was already checked for validity size_t hdrlen; uint8_t HeaderType, *data; lua_Integer idx = 1; lua_pushliteral(params.L, "exthdr"); lua_newtable(params.L); if (len>=sizeof(struct ip6_hdr)) { HeaderType = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; data=(uint8_t*)(ip6+1); len-=sizeof(struct ip6_hdr); while (len > 0) // need at least one byte for NextHeader field { switch (HeaderType) { case IPPROTO_HOPOPTS: case IPPROTO_ROUTING: case IPPROTO_DSTOPTS: case IPPROTO_MH: // mobility header case IPPROTO_HIP: // Host Identity Protocol Version v2 case IPPROTO_SHIM6: if (len < 2) return; // error hdrlen = 8 + (data[1] << 3); break; case IPPROTO_FRAGMENT: // fragment. length fixed to 8, hdrlen field defined as reserved hdrlen = 8; break; case IPPROTO_AH: // special case. length in ah header is in 32-bit words minus 2 if (len < 2) return; // error hdrlen = 8 + (data[1] << 2); break; case IPPROTO_NONE: // no next header default: // we found some meaningful payload. it can be tcp, udp, icmp or some another exotic shit goto end; } if (len < hdrlen) goto end; // error lua_pushinteger(params.L, idx++); lua_createtable(params.L, 0, 3); lua_pushf_int("type", HeaderType); HeaderType = *data; lua_pushf_int("next", HeaderType); lua_pushf_raw("data",data+2,hdrlen-2); lua_rawset(params.L,-3); // advance to the next header location len -= hdrlen; data += hdrlen; } } end: lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len) { LUA_STACK_GUARD_ENTER(params.L) lua_pushliteral(params.L, "ip6"); if (ip6) { lua_createtable(params.L, 0, 7); lua_pushf_lint("ip6_flow",ntohl(ip6->ip6_ctlun.ip6_un1.ip6_un1_flow)); lua_pushf_lint("ip6_plen",ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen)); lua_pushf_int("ip6_nxt",ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt); lua_pushf_int("ip6_hlim",ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim); lua_pushf_raw("ip6_src",&ip6->ip6_src,sizeof(struct in6_addr)); lua_pushf_raw("ip6_dst",&ip6->ip6_dst,sizeof(struct in6_addr)); lua_pushf_ip6exthdr(ip6,len); } else lua_pushnil(params.L); lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_push_dissect(const struct dissect *dis) { LUA_STACK_GUARD_ENTER(params.L) if (dis) { lua_createtable(params.L, 0, 9); lua_pushf_iphdr(dis->ip, dis->len_l3); lua_pushf_ip6hdr(dis->ip6, dis->len_l3); lua_pushf_tcphdr(dis->tcp, dis->len_l4); lua_pushf_udphdr(dis->udp, dis->len_l4); lua_pushf_int("l4proto",dis->proto); lua_pushf_int("transport_len",dis->transport_len); lua_pushf_int("l3_len",dis->len_l3); lua_pushf_int("l4_len",dis->len_l4); lua_pushf_raw("payload",dis->data_payload,dis->len_payload); } else lua_pushnil(params.L); LUA_STACK_GUARD_LEAVE(params.L, 1) } void lua_pushf_dissect(const struct dissect *dis) { lua_pushliteral(params.L, "dis"); lua_push_dissect(dis); lua_rawset(params.L,-3); } void lua_pushf_ctrack_pos(const t_ctrack *ctrack, const t_ctrack_position *pos) { LUA_STACK_GUARD_ENTER(params.L) lua_pushf_lint("pcounter", pos->pcounter); lua_pushf_lint("pdcounter", pos->pdcounter); lua_pushf_lint("pbcounter", pos->pbcounter); if (pos->ip6flow) lua_pushf_int("ip6_flow", pos->ip6flow); if (ctrack->ipproto == IPPROTO_TCP) { lua_pushliteral(params.L, "tcp"); lua_createtable(params.L, 0, 11); lua_pushf_lint("seq0", pos->seq0); lua_pushf_lint("seq", pos->seq_last); lua_pushf_lint("rseq", pos->seq_last - pos->seq0); lua_pushf_bool("rseq_over_2G", pos->rseq_over_2G); lua_pushf_int("pos", pos->pos - pos->seq0); lua_pushf_int("uppos", pos->uppos - pos->seq0); lua_pushf_int("uppos_prev", pos->uppos_prev - pos->seq0); lua_pushf_int("winsize", pos->winsize); lua_pushf_int("winsize_calc", pos->winsize_calc); lua_pushf_int("scale", pos->scale); lua_pushf_int("mss", pos->mss); lua_rawset(params.L,-3); } LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_positions *tpos, bool bIncoming) { LUA_STACK_GUARD_ENTER(params.L) if (!tpos) tpos = &ctrack->pos; lua_pushliteral(params.L, "track"); if (ctrack) { lua_createtable(params.L, 0, 9); if (ctrack->incoming_ttl) lua_pushf_int("incoming_ttl", ctrack->incoming_ttl); else lua_pushf_nil("incoming_ttl"); lua_pushf_str("l7proto", l7proto_str(ctrack->l7proto)); lua_pushf_str("hostname", ctrack->hostname); lua_pushf_bool("hostname_is_ip", ctrack->hostname_is_ip); lua_pushf_reg("lua_state", ctrack->lua_state); lua_pushf_bool("lua_in_cutoff", ctrack->b_lua_in_cutoff); lua_pushf_bool("lua_out_cutoff", ctrack->b_lua_out_cutoff); lua_pushf_lint("t_start", (lua_Number)ctrack->t_start.tv_sec + ctrack->t_start.tv_nsec/1000000000.); lua_pushliteral(params.L, "pos"); lua_createtable(params.L, 0, 5); // orig, reply related to connection logical direction // for tcp orig is client (who connects), reply is server (who listens). // for orig is the first seen party, reply is another party lua_pushf_number("dt", (lua_Number)tpos->t_last.tv_sec - (lua_Number)ctrack->t_start.tv_sec + (tpos->t_last.tv_nsec - ctrack->t_start.tv_nsec)/1000000000.); lua_pushliteral(params.L, "client"); lua_newtable(params.L); lua_pushf_ctrack_pos(ctrack, &tpos->client); lua_rawset(params.L,-3); lua_pushliteral(params.L, "server"); lua_newtable(params.L); lua_pushf_ctrack_pos(ctrack, &tpos->server); lua_rawset(params.L,-3); // direct and reverse are adjusted for server mode. in server mode orig and reply are exchanged. lua_pushliteral(params.L, "direct"); lua_getfield(params.L, -2, (params.server ^ bIncoming) ? "server" : "client"); lua_rawset(params.L,-3); lua_pushliteral(params.L, "reverse"); lua_getfield(params.L, -2, (params.server ^ bIncoming) ? "client" : "server"); lua_rawset(params.L,-3); lua_rawset(params.L,-3); } else lua_pushnil(params.L); lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_args(const struct str2_list_head *args, int idx_desync, bool subst_prefix) { // var=val - pass val string // var=%val - subst 'val' blob // var=#val - subst 'val' blob length // var=\#val - no subst, skip '\' // var=\%val - no subst, skip '\' LUA_STACK_GUARD_ENTER(params.L) struct str2_list *arg; const char *var, *val; idx_desync = lua_absindex(params.L, idx_desync); lua_pushliteral(params.L,"arg"); lua_newtable(params.L); LIST_FOREACH(arg, args, next) { var = arg->str1; val = arg->str2 ? arg->str2 : ""; if (subst_prefix) { if (val[0]=='\\' && (val[1]=='%' || val[1]=='#')) // escape char lua_pushf_str(var, val+1); else if (val[0]=='%') lua_pushf_blob(idx_desync, var, val+1); else if (val[0]=='#') { lua_push_blob(idx_desync, val+1); lua_Integer len = lua_rawlen(params.L, -1); lua_pop(params.L,1); lua_pushf_int(var, len); } else lua_pushf_str(var, val); } else lua_pushf_str(var, val); } lua_rawset(params.L,-3); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_pos(const char *name, const struct packet_pos *pos) { LUA_STACK_GUARD_ENTER(params.L) char smode[2]="?"; lua_pushf_table(name); lua_getfield(params.L,-1,name); *smode=pos->mode; lua_pushf_str("mode",smode); lua_pushf_lint("pos",pos->pos); lua_pop(params.L,1); LUA_STACK_GUARD_LEAVE(params.L, 0) } void lua_pushf_range(const char *name, const struct packet_range *range) { LUA_STACK_GUARD_ENTER(params.L) lua_pushf_table(name); lua_getfield(params.L,-1,"range"); lua_pushf_bool("upper_cutoff",range->upper_cutoff); lua_pushf_pos("from", &range->from); lua_pushf_pos("to", &range->to); lua_pop(params.L,1); LUA_STACK_GUARD_LEAVE(params.L, 0) } static void lua_reconstruct_extract_options(lua_State *L, int idx, bool *badsum, bool *ip6_preserve_next, uint8_t *ip6_last_proto) { if (lua_isnoneornil(L,idx)) { if (badsum) *badsum = false; if (ip6_preserve_next) *ip6_preserve_next = false; if (ip6_last_proto) *ip6_last_proto = IPPROTO_NONE; } else { luaL_checktype(L, idx, LUA_TTABLE); if (badsum) { lua_getfield(L,idx,"badsum"); *badsum = lua_type(L,-1)!=LUA_TNIL && (lua_type(L,-1)!=LUA_TBOOLEAN || lua_toboolean(L,-1)); lua_pop(L,1); } if (ip6_preserve_next) { lua_getfield(L,idx,"ip6_preserve_next"); *ip6_preserve_next = lua_type(L,-1)!=LUA_TNIL && (lua_type(L,-1)!=LUA_TBOOLEAN || lua_toboolean(L,-1)); lua_pop(L,1); } if (ip6_last_proto) { lua_getfield(L,idx,"ip6_last_proto"); *ip6_last_proto = lua_type(L,-1)==LUA_TNIL ? IPPROTO_NONE : (uint8_t)lua_tointeger(L,-1); lua_pop(L,1); } } } static bool lua_reconstruct_ip6exthdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t proto, bool preserve_next) { LUA_STACK_GUARD_ENTER(params.L) // proto = last header type if (*lenip6_ctlun.ip6_un1.ip6_un1_nxt; uint8_t filled = sizeof(struct ip6_hdr); lua_getfield(params.L,idx,"exthdr"); if (lua_type(params.L,-1)==LUA_TTABLE) { lua_Integer idx=0; uint8_t next, type, *p, *data = (uint8_t*)(ip6+1); size_t l, left; left = *len - filled; for(;;) { lua_rawgeti(params.L,-1,++idx); if (lua_type(params.L,-1)==LUA_TNIL) { lua_pop(params.L, 1); break; } else { if (lua_type(params.L,-1)!=LUA_TTABLE) goto err2; lua_getfield(params.L,-1, "type"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err3; type = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,-1, "next"); next = lua_type(params.L,-1)==LUA_TNUMBER ? (uint8_t)lua_tointeger(params.L,-1) : IPPROTO_NONE; lua_pop(params.L, 1); lua_getfield(params.L,-1, "data"); if (lua_type(params.L,-1)!=LUA_TSTRING) goto err3; p=(uint8_t*)lua_tolstring(params.L,-1,&l); if (!l || (l+2)>left || ((type==IPPROTO_AH) ? (l<6 || ((l+2) & 3)) : ((l+2) & 7))) goto err3; memcpy(data+2,p,l); l+=2; data[0] = next; // may be overwritten later data[1] = (type==IPPROTO_AH) ? (l>>2)-2 : (l>>3)-1; if (!preserve_next) *last_proto = type; last_proto = data; // first byte of header holds type left -= l; data += l; filled += l; lua_pop(params.L, 2); } } } // set last header proto if (!preserve_next) *last_proto = proto; *len = filled; lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return true; err2: lua_pop(params.L, 2); goto err; err3: lua_pop(params.L, 3); err: LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t last_proto, bool preserve_next) { LUA_STACK_GUARD_ENTER(params.L) const char *p; size_t l; if (*lenip6_ctlun.ip6_un1.ip6_un1_flow = htonl(lua_type(params.L,-1)==LUA_TNUMBER ? (uint32_t)lua_tolint(params.L,-1) : 0x60000000); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6_plen"); ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6_nxt"); ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6_hlim"); ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6_src"); if (lua_type(params.L,-1)!=LUA_TSTRING) goto err; p = lua_tolstring(params.L,-1,&l); if (l!=sizeof(struct in6_addr)) goto err; ip6->ip6_src = *(struct in6_addr*)p; lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6_dst"); if (lua_type(params.L,-1)!=LUA_TSTRING) goto err; p = lua_tolstring(params.L,-1,&l); if (l!=sizeof(struct in6_addr)) goto err; ip6->ip6_dst = *(struct in6_addr*)p; lua_pop(params.L, 1); return lua_reconstruct_ip6exthdr(idx, ip6, len, last_proto, preserve_next); err: lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } static int luacall_reconstruct_ip6hdr(lua_State *L) { lua_check_argc_range(L,"reconstruct_ip6hdr",1,2); LUA_STACK_GUARD_ENTER(L) char data[512]; size_t len=sizeof(data); uint8_t last_proto; bool preserve_next; lua_reconstruct_extract_options(L, 2, NULL, &preserve_next, &last_proto); if (!lua_reconstruct_ip6hdr(1,(struct ip6_hdr*)data, &len, last_proto, preserve_next)) luaL_error(L, "invalid data for ip6hdr"); lua_pushlstring(params.L,data,len); LUA_STACK_GUARD_RETURN(L,1) } bool lua_reconstruct_iphdr(int idx, struct ip *ip, size_t *len) { const char *p; size_t l, lopt=0; LUA_STACK_GUARD_ENTER(params.L) if (*lenip_v = IPVERSION; lua_getfield(params.L,idx,"ip_tos"); ip->ip_tos = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_len"); ip->ip_len = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_id"); ip->ip_id = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_off"); ip->ip_off = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_ttl"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; ip->ip_ttl = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_p"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; ip->ip_p = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_src"); if (lua_type(params.L,-1)!=LUA_TSTRING) goto err; p = lua_tolstring(params.L,-1,&l); if (l!=sizeof(struct in_addr)) goto err; ip->ip_src = *(struct in_addr*)p; lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip_dst"); if (lua_type(params.L,-1)!=LUA_TSTRING) goto err; p = lua_tolstring(params.L,-1,&l); if (l!=sizeof(struct in_addr)) goto err; ip->ip_dst = *(struct in_addr*)p; lua_pop(params.L, 1); lua_getfield(params.L,idx,"options"); if (lua_type(params.L,-1)==LUA_TSTRING) { p = lua_tolstring(params.L,-1,&lopt); if (p && lopt) { if (lopt>40 || ((sizeof(struct ip) + ((lopt+3)&~3)) > *len)) goto err; memcpy(ip+1,p,lopt); memset(((uint8_t*)ip) + sizeof(struct ip) + lopt, 0, (4-lopt&3)&3); lopt = (lopt+3) & ~3; } } lua_pop(params.L, 1); *len = sizeof(struct ip) + lopt; ip->ip_hl = *len >> 2; ip4_fix_checksum(ip); LUA_STACK_GUARD_LEAVE(params.L, 0) return true; err: lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } static int luacall_reconstruct_iphdr(lua_State *L) { lua_check_argc(L,"reconstruct_iphdr",1); LUA_STACK_GUARD_ENTER(L) char data[60]; size_t l = sizeof(data); if (!lua_reconstruct_iphdr(1,(struct ip*)&data,&l)) luaL_error(L, "invalid data for iphdr"); lua_pushlstring(params.L,data,l); LUA_STACK_GUARD_RETURN(L,1) } static bool lua_reconstruct_tcphdr_options(int idx, struct tcphdr *tcp, size_t *len) { if (*len40) left=40; // max size of tcp options for (;;) { lua_rawgeti(params.L,-1,++idx); if (lua_type(params.L,-1)==LUA_TNIL) { lua_pop(params.L, 1); break; } else { // uses 'key' (at index -2) and 'value' (at index -1) if (!left || lua_type(params.L,-1)!=LUA_TTABLE) goto err2; lua_getfield(params.L,-1, "kind"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err3; kind = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); switch(kind) { case TCP_KIND_END: *data = kind; data++; left--; filled++; lua_pop(params.L, 1); goto end; case TCP_KIND_NOOP: *data = kind; data++; left--; filled++; break; default: lua_getfield(params.L,-1, "data"); l = 0; p = lua_type(params.L,-1)==LUA_TSTRING ? (uint8_t*)lua_tolstring(params.L,-1,&l) : NULL; if ((2+l)>left) goto err3; if (p) memcpy(data+2,p,l); l+=2; data[0] = kind; data[1] = (uint8_t)l; left -= l; data += l; filled += l; lua_pop(params.L, 1); } lua_pop(params.L, 1); } } end: while(filled & 3) { if (!left) goto err1; *data = TCP_KIND_NOOP; data++; left--; filled++; } } tcp->th_off = filled>>2; *len = filled; lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return true; err1: lua_pop(params.L, 1); goto err; err2: lua_pop(params.L, 2); goto err; err3: lua_pop(params.L, 3); err: LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } bool lua_reconstruct_tcphdr(int idx, struct tcphdr *tcp, size_t *len) { if (*lenth_sport = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_dport"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; tcp->th_dport = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_seq"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; tcp->th_seq = htonl((uint32_t)lua_tolint(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_ack"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; tcp->th_ack = htonl((uint32_t)lua_tolint(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_x2"); tcp->th_x2 = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_flags"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; tcp->th_flags = (uint8_t)lua_tointeger(params.L,-1); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_win"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; tcp->th_win = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_sum"); tcp->th_sum = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"th_urp"); tcp->th_urp = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); tcp->th_off = 5; LUA_STACK_GUARD_LEAVE(params.L, 0) return lua_reconstruct_tcphdr_options(idx, tcp, len); err: lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } static int luacall_reconstruct_tcphdr(lua_State *L) { lua_check_argc(L,"reconstruct_tcphdr",1); LUA_STACK_GUARD_ENTER(L) char data[60]; size_t len=sizeof(data); if (!lua_reconstruct_tcphdr(1,(struct tcphdr*)data,&len)) luaL_error(L, "invalid data for tcphdr"); lua_pushlstring(params.L,data,len); LUA_STACK_GUARD_RETURN(L,1) } bool lua_reconstruct_udphdr(int idx, struct udphdr *udp) { if (lua_type(params.L,-1)!=LUA_TTABLE) return false; LUA_STACK_GUARD_ENTER(params.L) lua_getfield(params.L,idx,"uh_sport"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; udp->uh_sport = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"uh_dport"); if (lua_type(params.L,-1)!=LUA_TNUMBER) goto err; udp->uh_dport = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"uh_ulen"); udp->uh_ulen = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); lua_getfield(params.L,idx,"uh_sum"); udp->uh_sum = htons((uint16_t)lua_tointeger(params.L,-1)); lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return true; err: lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } static int luacall_reconstruct_udphdr(lua_State *L) { LUA_STACK_GUARD_ENTER(L) lua_check_argc(L,"reconstruct_udphdr",1); struct udphdr udp; if (!lua_reconstruct_udphdr(1,&udp)) luaL_error(L, "invalid data for udphdr"); lua_pushlstring(params.L,(char*)&udp,sizeof(udp)); LUA_STACK_GUARD_RETURN(L,1) } uint8_t lua_ip6_l4proto_from_dissect(int idx) { int type; lua_getfield(params.L,idx,"tcp"); type=lua_type(params.L,-1); lua_pop(params.L,1); if (type==LUA_TTABLE) return IPPROTO_TCP; lua_getfield(params.L,idx,"udp"); type=lua_type(params.L,-1); lua_pop(params.L,1); return type==LUA_TTABLE ? IPPROTO_UDP : IPPROTO_NONE; } bool lua_reconstruct_dissect(int idx, uint8_t *buf, size_t *len, bool badsum, bool ip6_preserve_next) { uint8_t *data = buf; size_t l,lpayload,l3,left = *len; struct ip *ip=NULL; struct ip6_hdr *ip6=NULL; struct tcphdr *tcp=NULL; struct udphdr *udp=NULL; const char *p; LUA_STACK_GUARD_ENTER(params.L) idx = lua_absindex(params.L, idx); lua_getfield(params.L,idx,"ip"); l = left; if (lua_type(params.L,-1)==LUA_TTABLE) { ip = (struct ip*)data; if (!lua_reconstruct_iphdr(-1, ip, &l)) { DLOG_ERR("reconstruct_dissect: bad ip\n"); goto err; } ip4_fix_checksum(ip); } else { lua_pop(params.L, 1); lua_getfield(params.L,idx,"ip6"); if (lua_type(params.L,-1)!=LUA_TTABLE) goto err; ip6 = (struct ip6_hdr*)data; if (!lua_reconstruct_ip6hdr(-1, ip6, &l, lua_ip6_l4proto_from_dissect(idx), ip6_preserve_next)) { DLOG_ERR("reconstruct_dissect: bad ip6\n"); goto err; } } l3=l; data+=l; left-=l; lua_pop(params.L, 1); lua_getfield(params.L,idx,"tcp"); l = left; if (lua_type(params.L,-1)==LUA_TTABLE) { tcp = (struct tcphdr*)data; if (!lua_reconstruct_tcphdr(-1, tcp, &l)) { DLOG_ERR("reconstruct_dissect: bad tcp\n"); goto err; } } else { lua_pop(params.L, 1); lua_getfield(params.L,idx,"udp"); l = sizeof(struct udphdr); if (lua_type(params.L,-1)!=LUA_TTABLE || leftuh_ulen = htons((uint16_t)(lpayload+sizeof(struct udphdr))); udp_fix_checksum(udp,l-l3,ip,ip6); if (badsum) udp->uh_sum ^= 1 + (random() % 0xFFFF); } if (tcp) { tcp_fix_checksum(tcp,l-l3,ip,ip6); if (badsum) tcp->th_sum ^= 1 + (random() % 0xFFFF); } if (ip) { if (ntohs(ip->ip_off) & (IP_OFFMASK|IP_MF)) { // fragmentation. caller should set ip_len, ip_off and IP_MF correctly. C code moves and shrinks constructed ip payload uint16_t iplen = ntohs(ip->ip_len); uint16_t off = (ntohs(ip->ip_off) & IP_OFFMASK)<<3; size_t frag_start = l3 + off; if (iplenl) { DLOG_ERR("ipv4 frag : invalid ip_len\n"); goto err; } if (frag_start>l) { DLOG_ERR("ipv4 frag : fragment offset is outside of the packet\n"); goto err; } if (off) memmove(buf+l3,buf+l3+off,iplen-l3); l = iplen; // shrink packet to iplen } else ip->ip_len = htons((uint16_t)l); ip4_fix_checksum(ip); } else if (ip6) { // data points to reconstructed packet's end uint8_t *frag = proto_find_ip6_exthdr(ip6, l, IPPROTO_FRAGMENT); if (frag) { uint16_t plen = ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); // without ipv6 base header uint16_t off = ntohs(((struct ip6_frag *)frag)->ip6f_offlg) & 0xFFF8; uint8_t *endfrag = frag + 8; size_t size_unfragmentable = endfrag - (uint8_t*)ip6 - sizeof(struct ip6_hdr); if (size_unfragmentable > plen) { DLOG_ERR("ipv6 frag : invalid ip6_plen\n"); goto err; } size_t size_fragmentable = plen - size_unfragmentable; if ((endfrag + off + size_fragmentable) > data) { DLOG_ERR("ipv6 frag : fragmentable part is outside of the packet\n"); goto err; } if (off) memmove(endfrag, endfrag + off, size_fragmentable); l = sizeof(struct ip6_hdr) + plen; } else ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons((uint16_t)(l-sizeof(struct ip6_hdr))); } *len = l; LUA_STACK_GUARD_LEAVE(params.L, 0) return true; err: lua_pop(params.L, 1); LUA_STACK_GUARD_LEAVE(params.L, 0) return false; } static int luacall_reconstruct_dissect(lua_State *L) { // reconstruct_dissect(data, reconstruct_opts) lua_check_argc_range(L,"reconstruct_dissect",1,2); LUA_STACK_GUARD_ENTER(L) uint8_t buf[RECONSTRUCT_MAX_SIZE]; size_t l = sizeof(buf); bool ip6_preserve_next, badsum; lua_reconstruct_extract_options(params.L, 2, &badsum, &ip6_preserve_next, NULL); if (!lua_reconstruct_dissect(1, buf, &l, badsum, ip6_preserve_next)) luaL_error(L, "invalid dissect data"); lua_pushlstring(params.L,(char*)buf,l); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_dissect(lua_State *L) { // dissect(packet_data) lua_check_argc(L,"dissect",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (const uint8_t*)luaL_checklstring(L, 1, &len); struct dissect dis; proto_dissect_l3l4(data, len, &dis); lua_push_dissect(&dis); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_csum_ip4_fix(lua_State *L) { // csum_ip4_fix(ip_header) returns ip_header lua_check_argc(L,"csum_ip4_fix",1); LUA_STACK_GUARD_ENTER(L) size_t l; const uint8_t *data = (const uint8_t*)luaL_checklstring(L, 1, &l); if (l>60 || !proto_check_ipv4(data, l)) luaL_error(L, "invalid ip header"); uint8_t data2[60]; memcpy(data2, data, l); ip4_fix_checksum((struct ip*)data2); lua_pushlstring(params.L,(char*)data2,l); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_csum_tcp_fix(lua_State *L) { // csum_tcp_fix(ip_header, tcp_header, payload) returns tcp_header lua_check_argc(L,"csum_tcp_fix",3); LUA_STACK_GUARD_ENTER(L) size_t l_ip; const uint8_t *b_ip = (const uint8_t*)luaL_checklstring(L, 1, &l_ip); const struct ip *ip=NULL; const struct ip6_hdr *ip6=NULL; if (proto_check_ipv4(b_ip, l_ip)) ip = (struct ip*)b_ip; else if (proto_check_ipv6(b_ip, sizeof(struct ip6_hdr) + ntohs(((struct ip6_hdr*)b_ip)->ip6_ctlun.ip6_un1.ip6_un1_plen))) ip6 = (struct ip6_hdr*)b_ip; else luaL_error(L, "invalid ip header"); size_t l_tcp; const uint8_t *b_tcp = (const uint8_t*)luaL_checklstring(L, 2, &l_tcp); if (!proto_check_tcp(b_tcp, l_tcp)) luaL_error(L, "invalid tcp header"); size_t l_pl; const uint8_t *b_pl = (const uint8_t*)luaL_checklstring(L, 3, &l_pl); size_t l_tpl = l_tcp + l_pl; uint8_t *tpl = malloc(l_tpl); if (!tpl) luaL_error(L, "out of memory"); memcpy(tpl, b_tcp, l_tcp); memcpy(tpl+l_tcp, b_pl, l_pl); struct tcphdr *tcp = (struct tcphdr*)tpl; tcp_fix_checksum(tcp, l_tpl, ip, ip6); lua_pushlstring(L,(char*)tpl,l_tcp); free(tpl); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_csum_udp_fix(lua_State *L) { // csum_udp_fix(ip_header, udp_header, payload) returns udp_header lua_check_argc(L,"csum_udp_fix",3); LUA_STACK_GUARD_ENTER(L) size_t l_ip; const uint8_t *b_ip = (const uint8_t*)luaL_checklstring(L, 1, &l_ip); const struct ip *ip=NULL; const struct ip6_hdr *ip6=NULL; if (proto_check_ipv4(b_ip, l_ip)) ip = (struct ip*)b_ip; else if (proto_check_ipv6(b_ip, sizeof(struct ip6_hdr) + ntohs(((struct ip6_hdr*)b_ip)->ip6_ctlun.ip6_un1.ip6_un1_plen))) ip6 = (struct ip6_hdr*)b_ip; else luaL_error(L, "invalid ip header"); size_t l_udp; const uint8_t *b_udp = (const uint8_t*)luaL_checklstring(L, 2, &l_udp); if (!proto_check_udp(b_udp, ntohs(((struct udphdr*)b_udp)->uh_ulen))) luaL_error(L, "invalid udp header"); size_t l_pl; const uint8_t *b_pl = (const uint8_t*)luaL_checklstring(L, 3, &l_pl); size_t l_tpl = l_udp + l_pl; uint8_t *tpl = malloc(l_tpl); if (!tpl) luaL_error(L, "out of memory"); memcpy(tpl, b_udp, l_udp); memcpy(tpl+l_udp, b_pl, l_pl); struct udphdr *udp = (struct udphdr*)tpl; udp_fix_checksum(udp, l_tpl, ip, ip6); lua_pushlstring(L,(char*)tpl,l_udp); free(tpl); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_ntop(lua_State *L) { size_t l; const char *p; char s[40]; int af=0; lua_check_argc(L,"ntop",1); LUA_STACK_GUARD_ENTER(L) p=luaL_checklstring(L,1,&l); switch(l) { case sizeof(struct in_addr): af=AF_INET; break; case sizeof(struct in6_addr): af=AF_INET6; break; default: lua_pushnil(L); return 1; } if (!inet_ntop(af,p,s,sizeof(s))) luaL_error(L, "inet_ntop error"); lua_pushstring(L,s); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_pton(lua_State *L) { const char *p; char s[sizeof(struct in6_addr)]; lua_check_argc(L,"pton",1); LUA_STACK_GUARD_ENTER(L) p=luaL_checkstring(L,1); if (inet_pton(AF_INET,p,s)) lua_pushlstring(L,s,sizeof(struct in_addr)); else if (inet_pton(AF_INET6,p,s)) lua_pushlstring(L,s,sizeof(struct in6_addr)); else lua_pushnil(L); LUA_STACK_GUARD_RETURN(L,1) } static void lua_rawsend_extract_options(lua_State *L, int idx, int *repeats, uint32_t *fwmark, const char **ifout) { if (lua_isnoneornil(L,idx)) { if (repeats) *repeats = 1; if (fwmark) *fwmark = params.desync_fwmark; if (ifout) *ifout = NULL; } else { luaL_checktype(L, idx, LUA_TTABLE); if (repeats) { lua_getfield(L,idx,"repeats"); *repeats=(int)lua_tointeger(L,-1); if (!*repeats) *repeats=1; lua_pop(L,1); } if (fwmark) { lua_getfield(L,idx,"fwmark"); *fwmark=(uint32_t)lua_tolint(L,-1) | params.desync_fwmark; lua_pop(L,1); } if (ifout) { lua_getfield(L,idx,"ifout"); *ifout = lua_type(L,-1)==LUA_TSTRING ? lua_tostring(L,-1) : NULL; lua_pop(L,1); } } } static int luacall_rawsend(lua_State *L) { // bool rawsend(raw_data, {repeats, fwmark, ifout}) lua_check_argc_range(L,"rawsend",1,2); LUA_STACK_GUARD_ENTER(L) uint8_t *data; const char *ifout; size_t len; int repeats; uint32_t fwmark; sockaddr_in46 sa; bool b; data=(uint8_t*)luaL_checklstring(L,1,&len); lua_rawsend_extract_options(L,2,&repeats,&fwmark,&ifout); if (!extract_dst(data, len, (struct sockaddr*)&sa)) luaL_error(L, "bad ip4/ip6 header"); DLOG("rawsend repeats=%d size=%zu ifout=%s fwmark=%08X\n", repeats,len,ifout ? ifout : "",fwmark); b = rawsend_rep(repeats, (struct sockaddr*)&sa, fwmark, ifout, data, len); lua_pushboolean(L, b); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_rawsend_dissect(lua_State *L) { // rawsend(data, rawsend_opts, reconstruct_opts) lua_check_argc_range(L,"rawsend_dissect",1,3); LUA_STACK_GUARD_ENTER(L) uint8_t buf[RECONSTRUCT_MAX_SIZE]; size_t len=sizeof(buf); const char *ifout; int repeats; uint32_t fwmark; sockaddr_in46 sa; bool b, badsum, ip6_preserve_next; luaL_checktype(L,1,LUA_TTABLE); lua_rawsend_extract_options(L,2, &repeats, &fwmark, &ifout); lua_reconstruct_extract_options(params.L, 3, &badsum, &ip6_preserve_next, NULL); if (!lua_reconstruct_dissect(1, buf, &len, badsum, ip6_preserve_next)) luaL_error(L, "invalid dissect data"); if (!extract_dst(buf, len, (struct sockaddr*)&sa)) luaL_error(L, "bad ip4/ip6 header"); DLOG("rawsend_dissect repeats=%d size=%zu badsum=%u ifout=%s fwmark=%08X\n", repeats,len,badsum,ifout ? ifout : "",fwmark); b = rawsend_rep(repeats, (struct sockaddr*)&sa, fwmark, ifout, buf, len); lua_pushboolean(L, b); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_resolve_pos(lua_State *L) { // resolve_pos(blob,l7payload_type,marker[,zero_based_pos]) lua_check_argc_range(L,"resolve_pos",3,4); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); const char *sl7payload = luaL_checkstring(L,2); const char *smarker = luaL_checkstring(L,3); bool bZeroBased = argc>=4 && lua_toboolean(L,4); t_l7payload l7payload = l7payload_from_name(sl7payload); if (l7payload==L7P_INVALID) luaL_error(L, "bad payload type : '%s'", sl7payload); struct proto_pos marker; if (!posmarker_parse(smarker,&marker)) luaL_error(L, "bad marker : '%s'", smarker); ssize_t pos=ResolvePos(data, len, l7payload, &marker); if (pos==POS_NOT_FOUND) lua_pushnil(L); else lua_pushinteger(L,pos+!bZeroBased); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_resolve_multi_pos(lua_State *L) { // resolve_multi_pos(blob,l7payload_type,marker_list[,zero_based_pos]) lua_check_argc_range(L,"resolve_multi_pos",3,4); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); const char *sl7payload = luaL_checkstring(L,2); const char *smarkers = luaL_checkstring(L,3); bool bZeroBased = argc>=4 && lua_toboolean(L,4); t_l7payload l7payload = l7payload_from_name(sl7payload); if (l7payload==L7P_INVALID) luaL_error(L, "bad payload type : '%s'", sl7payload); struct proto_pos markers[128]; ssize_t pos[sizeof(markers)/sizeof(*markers)]; int i, ctpos, ctm = sizeof(markers)/sizeof(*markers); if (!posmarker_list_parse(smarkers,markers,&ctm)) luaL_error(L, "bad marker list"); ResolveMultiPos(data, len, l7payload, markers, ctm, pos, &ctpos); lua_newtable(L); for(i=0;i=4 && lua_toboolean(L,4); bool bZeroBased = argc>=5 && lua_toboolean(L,5); t_l7payload l7payload = l7payload_from_name(sl7payload); if (l7payload==L7P_INVALID) luaL_error(L, "bad payload type : '%s'", sl7payload); struct proto_pos markers[2]; ssize_t pos[sizeof(markers)/sizeof(*markers)]; int ctm = sizeof(markers)/sizeof(*markers); if (!posmarker_list_parse(smarkers,markers,&ctm)) luaL_error(L, "bad marker list"); if (ctm!=2) luaL_error(L, "resolve_range require 2 markers"); pos[0] = ResolvePos(data, len, l7payload, markers); pos[1] = ResolvePos(data, len, l7payload, markers+1); if (pos[0]==POS_NOT_FOUND && pos[1]==POS_NOT_FOUND || bStrict && (pos[0]==POS_NOT_FOUND || pos[1]==POS_NOT_FOUND)) { lua_pushnil(L); return 1; } if (pos[0]==POS_NOT_FOUND) pos[0] = 0; if (pos[1]==POS_NOT_FOUND) pos[1] = len-1; if (pos[0]>pos[1]) { lua_pushnil(L); return 1; } lua_newtable(L); lua_pushi_int(1,pos[0]+!bZeroBased); lua_pushi_int(2,pos[1]+!bZeroBased); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_is_tls_client_hello(lua_State *L) { // (blob,partialOK) lua_check_argc_range(L,"tls_record_is_tls_client_hello",1,2); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool bPartialOK = argc>=2 && lua_toboolean(L,2); lua_pushboolean(L,IsTLSClientHello(data,len,bPartialOK)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_is_tls_server_hello(lua_State *L) { // (blob,partialOK) lua_check_argc_range(L,"tls_record_is_tls_server_hello",1,2); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool bPartialOK = argc>=2 && lua_toboolean(L,2); lua_pushboolean(L,IsTLSServerHello(data,len,bPartialOK)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_is_tls_client_hello(lua_State *L) { // (blob,partialOK) lua_check_argc_range(L,"tls_handshake_is_tls_client_hello",1,2); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool bPartialOK = argc>=2 && lua_toboolean(L,2); lua_pushboolean(L,IsTLSHandshakeClientHello(data,len,bPartialOK)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_is_tls_server_hello(lua_State *L) { // (blob,partialOK) lua_check_argc_range(L,"tls_handshake_is_tls_server_hello",1,2); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool bPartialOK = argc>=2 && lua_toboolean(L,2); lua_pushboolean(L,IsTLSHandshakeServerHello(data,len,bPartialOK)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_find_ext(lua_State *L) { // (blob,type,partialOK) lua_check_argc_range(L,"tls_record_find_ext",2,3); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len, len_ext; const uint8_t *ext, *data = (uint8_t*)luaL_checklstring(L,1,&len); luaL_checktype(L,2,LUA_TNUMBER); uint16_t type = (uint16_t)lua_tointeger(L,2); bool bPartialOK = argc>=3 && lua_toboolean(L,3); bool b = TLSFindExt(data, len, type, &ext, &len_ext, bPartialOK); lua_pushinteger(L,b ? ext-data+1 : 0); lua_pushinteger(L,b ? len_ext : 0); LUA_STACK_GUARD_RETURN(L,2) } static int luacall_tls_handshake_find_ext(lua_State *L) { // (blob,type,partialOK) lua_check_argc_range(L,"tls_handshake_find_ext",2,3); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t len, len_ext; const uint8_t *ext, *data = (uint8_t*)luaL_checklstring(L,1,&len); luaL_checktype(L,2,LUA_TNUMBER); uint16_t type = (uint16_t)lua_tointeger(L,2); bool bPartialOK = argc>=3 && lua_toboolean(L,3); bool b = TLSFindExtInHandshake(data, len, type, &ext, &len_ext, bPartialOK); lua_pushinteger(L,b ? ext-data+1 : 0); lua_pushinteger(L,b ? len_ext : 0); LUA_STACK_GUARD_RETURN(L,2) } static int luacall_tls_record_find_extlen(lua_State *L) { // (blob) lua_check_argc(L,"tls_record_find_extlen",1); LUA_STACK_GUARD_ENTER(L) size_t len, offset; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool b = TLSFindExtLen(data, len, &offset); lua_pushinteger(L,b ? offset+1 : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_find_extlen(lua_State *L) { // (blob) lua_check_argc(L,"tls_handshake_find_extlen",1); LUA_STACK_GUARD_ENTER(L) size_t len, offset; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); bool b = TLSFindExtLenOffsetInHandshake(data, len, &offset); lua_pushinteger(L,b ? offset+1 : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_len(lua_State *L) { // (blob) lua_check_argc(L,"tls_record_len",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushinteger(L,IsTLSHello(data,len,0,true) ? TLSRecordLen(data) : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_data_len(lua_State *L) { // (blob) lua_check_argc(L,"tls_record_data_len",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushinteger(L,IsTLSHello(data,len,0,true) ? TLSRecordDataLen(data) : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_record_is_full(lua_State *L) { // (blob) lua_check_argc(L,"tls_record_is_full",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushboolean(L,IsTLSHello(data,len,0,true) && IsTLSRecordFull(data,len) ); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_len(lua_State *L) { // (blob) lua_check_argc(L,"tls_handshake_len",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushinteger(L,IsTLSHandshakeHello(data,len,0,true) ? TLSHandshakeLen(data) : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_data_len(lua_State *L) { // (blob) lua_check_argc(L,"tls_handshake_data_len",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushinteger(L,IsTLSHandshakeHello(data,len,0,true) ? TLSHandshakeDataLen(data) : 0); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_handshake_is_full(lua_State *L) { // (blob) lua_check_argc(L,"tls_handshake_is_full",1); LUA_STACK_GUARD_ENTER(L) size_t len; const uint8_t *data = (uint8_t*)luaL_checklstring(L,1,&len); lua_pushboolean(L,IsTLSHandshakeFull(data,len)); LUA_STACK_GUARD_RETURN(L,1) } static int luacall_tls_mod(lua_State *L) { // (blob, modlist, payload) lua_check_argc_range(L,"tls_mod",2,3); LUA_STACK_GUARD_ENTER(L) int argc=lua_gettop(L); size_t fake_tls_len; bool bRes; const uint8_t *fake_tls = (uint8_t*)luaL_checklstring(L,1,&fake_tls_len); const char *modlist = luaL_checkstring(L,2); size_t payload_len = 0; const uint8_t *payload = NULL; if (argc>=3 && lua_type(L,3)!=LUA_TNIL) payload = (uint8_t*)luaL_checklstring(L,3,&payload_len); struct fake_tls_mod mod; if (!TLSMod_parse_list(modlist, &mod)) luaL_error(L, "invalid tls mod list : '%s'", modlist); if (mod.mod) { size_t newlen = fake_tls_len, maxlen = fake_tls_len + sizeof(mod.sni) + 4; uint8_t *newtls = malloc(maxlen); if (!newtls) luaL_error(L, "out of memory"); memcpy(newtls, fake_tls, newlen); bRes = TLSMod(&mod, payload, payload_len, newtls, &newlen, maxlen); lua_pushlstring(L,(char*)newtls,newlen); free(newtls); } else { // no mod. push it back lua_pushlstring(L,(char*)fake_tls,fake_tls_len); bRes = true; } lua_pushboolean(L, bRes); LUA_STACK_GUARD_RETURN(L,2) } // ---------------------------------------- void lua_shutdown() { if (params.L) { DLOG("LUA SHUTDOWN\n"); // conntrack holds lua state. must clear it before lua shoudown ConntrackPoolDestroy(¶ms.conntrack); lua_close(params.L); params.L=NULL; } } #if LUA_VERSION_NUM >= 504 static void lua_warn(void *ud, const char *msg, int tocont) { DLOG_CONDUP("LUA WARNING: %s\n",msg); } #endif static void lua_perror(lua_State *L) { if (lua_isstring(L, -1)) { const char *error_message = lua_tostring(L, -1); DLOG_ERR("LUA ERROR: %s\n", error_message); } lua_pop(L, 1); } static int lua_panic (lua_State *L) { lua_perror(L); DLOG_ERR("LUA PANIC: THIS IS FATAL. DYING.\n"); exit(100); return 0; } static bool lua_basic_init() { lua_shutdown(); if (!(params.L = luaL_newstate())) { DLOG_ERR("LUA INIT ERROR\n"); return false; } unsigned int ver; #if LUA_VERSION_NUM >= 504 ver = (unsigned int)lua_version(params.L); #elif LUA_VERSION_NUM >= 502 ver = (unsigned int)*lua_version(params.L); #else ver = LUA_VERSION_NUM; #endif #ifdef LUAJIT_VERSION #ifdef OPENRESTY_LUAJIT #define LJSUBVER " OpenResty" #else #define LJSUBVER "" #endif DLOG_CONDUP("LUA v%u.%u %s%s\n",ver/100,ver%100, LUAJIT_VERSION, LJSUBVER); #else DLOG_CONDUP("LUA v%u.%u\n",ver/100,ver%100); #endif #if LUA_VERSION_NUM >= 504 lua_setwarnf(params.L,lua_warn,NULL); #endif lua_atpanic(params.L,lua_panic); luaL_openlibs(params.L); /* Load Lua libraries */ return true; } static bool lua_desync_functions_exist() { struct desync_profile_list *dpl; struct func_list *func; LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { LIST_FOREACH(func, &dpl->dp.lua_desync, next) { lua_getglobal(params.L, func->func); if (!lua_isfunction(params.L,-1)) { lua_pop(params.L,1); DLOG_ERR("desync function '%s' does not exist\n",func->func); return false; } lua_pop(params.L,1); } } return true; } bool lua_test_init_script_files(void) { struct str_list *str; LIST_FOREACH(str, ¶ms.lua_init_scripts, next) { if (str->str[0]=='@' && !file_open_test(str->str+1, O_RDONLY)) { #ifndef __CYGWIN__ int e = errno; #endif DLOG_ERR("LUA file '%s' not accessible\n", str->str+1); #ifndef __CYGWIN__ if (e==EACCES) DLOG_ERR("I drop my privileges and do not run Lua as root\ncheck file permissions and +x rights on all directories in the path\n"); #endif return false; } } return true; } static bool lua_init_scripts(void) { struct str_list *str; int status; LIST_FOREACH(str, ¶ms.lua_init_scripts, next) { if (params.debug) { if (str->str[0]=='@') DLOG("LUA RUN FILE: %s\n",str->str+1); else { char s[128]; snprintf(s,sizeof(s),"%s",str->str); DLOG("LUA RUN STR: %s\n",s); } } if ((status = str->str[0]=='@' ? luaL_dofile(params.L, str->str+1) : luaL_dostring(params.L, str->str))) { lua_perror(params.L); return false; } } return true; } static void lua_sec_harden(void) { LUA_STACK_GUARD_ENTER(params.L) // remove unwanted functions. lua scripts are not intended to execute files const struct { const char *global, *field, *field2; } bad[] = { {"os","execute",NULL}, {"io","popen",NULL}, {"package","loadlib",NULL}, {"debug", NULL, NULL}, {"package", "loaded", "debug"} }; DLOG("LUA REMOVE:"); for (int i=0;iname, blob->size); lua_pushlstring(params.L, (char*)blob->data, blob->size); lua_setglobal(params.L, blob->name); blob_destroy(blob); } LUA_STACK_GUARD_LEAVE(params.L, 0) } static void lua_init_const(void) { LUA_STACK_GUARD_ENTER(params.L) const struct { const char *name, *v; } cstr[] = { {"NFQWS2_VER",params.verstr} }; DLOG("LUA STR:"); for (int i=0;i= params.lua_gc) { int kb1 = lua_gc(params.L, LUA_GCCOUNT, 0); lua_gc(params.L, LUA_GCCOLLECT, 0); int kb2 = lua_gc(params.L, LUA_GCCOUNT, 0); DLOG("\nLUA GARBAGE COLLECT: %dK => %dK\n",kb1,kb2); gc_time = now; } } }