diff --git a/configure.ac b/configure.ac index 80237556b44..c8beec024ae 100644 --- a/configure.ac +++ b/configure.ac @@ -1379,6 +1379,12 @@ no) ;; esac +AS_IF([ test "x${enable_lua_support}" = "xno"], [ + AC_MSG_ERROR([lua required but not found]) + ], [ + ]) + +TS_ADDTO(CPPFLAGS, [$LUA_CFLAGS]) AC_SUBST(LUA_CFLAGS) AC_SUBST(LUA_LIBS) AC_MSG_CHECKING([whether to enable Lua support]) @@ -1389,7 +1395,7 @@ AC_MSG_RESULT([$enable_lua_support]) # On Darwin LuaJIT requires magic link options, otherwise it will crash in luaL_openlibs() at startup. See # http://luajit.org/install.html. case $host_os in - darwin) + darwin*) if test "x${enable_lua_support}" = "xLuaJIT"; then LUA_LUAJIT_LDFLAGS="-Wl,-pagezero_size,10000 -Wl,-image_base,100000000" fi diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 8c4b8882320..f2455f47abf 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -23,6 +23,8 @@ #include "libts.h" #include "I_Layout.h" #include "P_Net.h" +#include "luaConfig.h" +#include "lua.hpp" #include #include @@ -632,6 +634,39 @@ ssl_extract_certificate( return true; } +static int +SSLUtils_lua_ssl_store_ssl_context(lua_State *L) { + const SSLConfigParams * params; + SSLCertLookup * lookup; + xptr dest_ip, ssl_key_name, ssl_ca_name, ssl_cert_name; + params = (const SSLConfigParams *) lua_touserdata(L, lua_upvalueindex(1)); + lookup = (SSLCertLookup *) lua_touserdata(L, lua_upvalueindex(2)); +#define LUAGETF(name) do { \ + lua_getfield(L,-1,#name); \ + name = (char *)lua_tostring(L,1); \ + lua_pop(L,1); \ +} while(0) + LUAGETF(dest_ip); + LUAGETF(ssl_key_name); + LUAGETF(ssl_ca_name); + LUAGETF(ssl_cert_name); + if(!ssl_store_ssl_context(params, lookup, dest_ip, ssl_cert_name, ssl_ca_name, ssl_key_name)) + lua_pushboolean(L,0); + else + lua_pushboolean(L,1); + return 1; +} + +void +SSLUtils_lua_ssl_context( + lua_State * L, + const SSLConfigParams * params, + SSLCertLookup * lookup) { + lua_pushlightuserdata(L, (void *)params); + lua_pushlightuserdata(L, (void *)lookup); + lua_pushcclosure(L, SSLUtils_lua_ssl_store_ssl_context, 2); +} + bool SSLParseCertificateConfiguration( const SSLConfigParams * params, @@ -702,6 +737,11 @@ SSLParseCertificateConfiguration( line = tokLine(NULL, &tok_state); } + + lua_State *L = globalLuaConfig.getL(); + SSLUtils_lua_ssl_context(L, params, lookup); + globalLuaConfig.call(L, "config_ssl", 1); + // We *must* have a default context even if it can't possibly work. The default context is used to // bootstrap the SSL handshake so that we can subsequently do the SNI lookup to switch to the real // context. diff --git a/proxy/Main.cc b/proxy/Main.cc index 2d34d5c537d..c9156d5ce90 100644 --- a/proxy/Main.cc +++ b/proxy/Main.cc @@ -89,6 +89,7 @@ extern "C" int plock(int); #include "XmlUtils.h" #include "I_Tasks.h" #include "InkAPIInternal.h" +#include "luaConfig.h" #include @@ -363,6 +364,8 @@ initialize_process_manager() LibRecordsConfigInit(); RecordsConfigOverrideFromEnvironment(); } + + luaConfigInit(); // // Start up manager // diff --git a/proxy/Makefile.am b/proxy/Makefile.am index 25651002406..34a955745d5 100644 --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -52,7 +52,8 @@ AM_CPPFLAGS = \ -I$(srcdir)/api/ts \ -I. \ -I./api/ts \ - -I$(top_srcdir)/lib + -I$(top_srcdir)/lib \ + $(LUA_CFLAGS) noinst_HEADERS = \ ConfigParse.h \ @@ -103,6 +104,8 @@ traffic_server_SOURCES = \ InkXml.h \ IPAllow.cc \ IPAllow.h \ + luaConfig.cc \ + luaConfig.h \ Main.cc \ Main.h \ ParentSelection.cc \ @@ -136,7 +139,7 @@ if BUILD_TESTS RegressionSM.cc endif -traffic_server_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@ +traffic_server_LDFLAGS = @LUA_LUAJIT_LDFLAGS@ @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@ traffic_server_LDADD = \ http/libhttp.a \ http/remap/libhttp_remap.a \ @@ -161,6 +164,7 @@ traffic_server_LDADD = \ $(top_builddir)/iocore/eventsystem/libinkevent.a \ $(which_libts) \ @hwloc_LIBS@ \ + @LUA_LIBS@ \ @LIBPCRE@ \ @LIBSSL@ \ @LIBTCL@ \ @@ -173,10 +177,6 @@ traffic_server_LDADD = \ @LIBPROFILER@ \ -lm -if BUILD_LUA_SUPPORT -traffic_server_LDFLAGS += @LUA_LUAJIT_LDFLAGS@ -endif - traffic_logcat_SOURCES = \ logcat.cc \ signals.cc \ @@ -238,9 +238,10 @@ traffic_sac_SOURCES = \ InkAPI.cc \ FetchSM.cc \ InkIOCoreAPI.cc \ + luaConfig.cc \ InkXml.cc -traffic_sac_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@ +traffic_sac_LDFLAGS = @LUA_LUAJIT_LDFLAGS@ @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@ traffic_sac_LDADD = \ http/libhttp.a \ http/remap/libhttp_remap.a \ @@ -262,6 +263,7 @@ traffic_sac_LDADD = \ $(top_builddir)/iocore/eventsystem/libinkevent.a \ $(top_builddir)/lib/records/librecprocess.a \ $(top_builddir)/lib/ts/libtsutil.la \ + @LUA_LIBS@ \ @LIBRESOLV@ @LIBPCRE@ @LIBSSL@ @LIBTCL@ \ @LIBEXPAT@ @LIBDEMANGLE@ @LIBZ@ @LIBLZMA@ @LIBPROFILER@ -lm diff --git a/proxy/http/remap/UrlRewrite.cc b/proxy/http/remap/UrlRewrite.cc index f88df843879..442ba0c3ee6 100644 --- a/proxy/http/remap/UrlRewrite.cc +++ b/proxy/http/remap/UrlRewrite.cc @@ -32,6 +32,7 @@ #include "Tokenizer.h" #include "api/ts/remap.h" #include "UrlMappingPathIndex.h" +#include "luaConfig.h" #include "ink_string.h" #include "ink_cap.h" @@ -1040,6 +1041,527 @@ UrlRewrite::_addToStore(MappingsStore &store, url_mapping *new_mapping, RegexMap return retval; } +struct luabuilder_crutch { + int line; // fake line number for ordering + UrlRewrite *urlrewrite; + BUILD_TABLE_INFO *bti; +}; + +static int +UrlRewrite_lua_filterstuff(lua_State *L) { + int startN = 0; + bool flg; + const char *func; + const char *filtername; + struct luabuilder_crutch *lbc; + acl_filter_rule *rp, **rpp; + + lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1)); + func = lua_tostring(L, lua_upvalueindex(2)); + + // we were called as a method + if(lua_gettop(L) > 0 && lua_isuserdata(L,1)) startN++; + + if(lua_gettop(L) < (startN+1) || !lua_isstring(L,(startN+1))) + luaL_error(L, "filter function require a filter name"); + filtername = lua_tostring(L,(startN+1)); + + if(!strcmp(func, "deletefilter")) { + acl_filter_rule::delete_byname(&lbc->bti->rules_list, filtername); + return 0; + } + else if(!strcmp(func, "activatefilter")) { + if ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL) + luaL_error(L, "Undefined filter \"%s\" in directive \"%s\"", filtername, func); + acl_filter_rule::requeue_in_active_list(&lbc->bti->rules_list, rp); + return 0; + } + else if(!strcmp(func, "deactivatefilter")) { + if ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL) + luaL_error(L, "Undefined filter \"%s\" in directive \"%s\"", filtername, func); + acl_filter_rule::requeue_in_passive_list(&lbc->bti->rules_list, rp); + return 0; + } + else if(!strcmp(func, "definefilter")) { + const char *cstr = NULL; + int argc = 0; + char *argv[256]; + char errStrBuf[1024]; + + if(lua_gettop(L) != (startN+2) || !lua_istable(L,(startN+2))) + luaL_error(L, "Invalid arguments to UrlRewrite:definefilter"); + + lua_pushnil(L); /* first key */ + while (lua_next(L, (startN+2)) != 0) { + char pbuf[512]; + if(lua_istable(L,-1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-4), lua_tostring(L,-1)); + argv[argc++] = ats_strdup(pbuf); + lua_pop(L,1); + } + } + else { + snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-2), lua_tostring(L,-1)); + argv[argc++] = ats_strdup(pbuf); + } + if(argc >= 256) luaL_error(L, "too many arguments to definefilter"); + lua_pop(L, 1); + } + if(argc == 0) luaL_error(L, "filter require arguments"); + + flg = ((rp = acl_filter_rule::find_byname(lbc->bti->rules_list, filtername)) == NULL) ? true : false; + // coverity[alloc_arg] + if ((cstr = validate_filter_args(&rp, argv, argc, errStrBuf, sizeof(errStrBuf))) == NULL && rp) { + if (flg) { // new filter - add to list + Debug("url_rewrite", "[parse_directive] new rule \"%s\" was created", filtername); + for (rpp = &lbc->bti->rules_list; *rpp; rpp = &((*rpp)->next)); + (*rpp = rp)->name(filtername); + } + Debug("url_rewrite", "[parse_directive] %d argument(s) were added to rule \"%s\"", argc, filtername); + rp->add_argv(argc, argv); // store string arguments for future processing + } + for(;argc>0;argc--) ats_free(argv[argc-1]); + if(cstr) luaL_error(L, "filter issue: %s", cstr); + return 0; + } + luaL_error(L, "filter helper encountered impossible upvalue: %s\n", func); + return 0; +} + +int +UrlRewrite::lua_mapstuff(lua_State *L) { + int startN = 0, i = 0, argc = 0, nargs; + char *argv[256]; + const char *errStr; + char *tag = NULL; + struct luabuilder_crutch *lbc; + char errStrBuf[1024]; + bool alarm_already = false; + + // Vars to build the mapping + const char *fromScheme, *toScheme; + int fromSchemeLen, toSchemeLen; + const char *fromHost, *toHost; + int fromHostLen, toHostLen; + char *map_from = NULL, *map_from_start; + char *map_to = NULL, *map_to_start; + const char *tmp; // Appease the DEC compiler + char *fromHost_lower = NULL; + char *fromHost_lower_ptr = NULL; + char fromHost_lower_buf[1024]; + url_mapping *new_mapping = NULL; + mapping_type maptype = FORWARD_MAP; + referer_info *ri; + int origLength; + int length; + int rparse; + + RegexMapping* reg_map; + bool is_cur_mapping_regex; + bool add_result; + + lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1)); + maptype = (mapping_type)lua_tointeger(L, lua_upvalueindex(2)); + is_cur_mapping_regex = (bool)lua_toboolean(L, lua_upvalueindex(3)); + + // we were called as a method + nargs = lua_gettop(L); + if(nargs > 0 && lua_isuserdata(L,1)) startN++; + + if(nargs < (startN+2)) luaL_error(L, "wrong number of argument to mapping"); + if(!lua_isstring(L, (startN+1)) || !lua_isstring(L, (startN+2))) + luaL_error(L, "mapping requires to and from"); + + // bump our fake line number... + lbc->line++; + + // process parameters + + // loop through and create paramstrings + for(i=startN+2;i<=nargs;i++) { + char pbuf[512]; + if(lua_istable(L,i)) { + lua_pushnil(L); /* first key */ + while (lua_next(L, i) != 0) { + if(lua_istable(L,-1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + // internal tables use that outside table's key + if(lua_isboolean(L,-1) && lua_toboolean(L,-1)) + snprintf(pbuf, sizeof(pbuf), "%s", lua_tostring(L,-4)); + else + snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-4), lua_tostring(L,-1)); + argv[argc++] = ats_strdup(pbuf); + lua_pop(L,1); + if(argc >= 256) { + errStr = "too many arguments to mapping"; + goto LUA_MAP_ERROR; + } + } + } + else { + if(lua_isboolean(L,-1) && lua_toboolean(L,-1)) + snprintf(pbuf, sizeof(pbuf), "%s", lua_tostring(L,-2)); + else + snprintf(pbuf, sizeof(pbuf), "%s=%s", lua_tostring(L,-2), lua_tostring(L,-1)); + argv[argc++] = ats_strdup(pbuf); + } + lua_pop(L, 1); + if(argc >= 256) { + errStr = "too many arguments to mapping"; + goto LUA_MAP_ERROR; + } + } + } + } + + lbc->bti->remap_optflg = check_remap_option(argv, argc); + // if FORWARD_MAP and @map_with_referer is set, we need tup update + if(maptype == FORWARD_MAP && + (lbc->bti->remap_optflg & REMAP_OPTFLG_MAP_WITH_REFERER)) + maptype = FORWARD_MAP_REFERER; + + new_mapping = NEW(new url_mapping(lbc->line)); // use line # for rank for now + + // apply filter rules if we have to + if ((errStr = process_filter_opt(new_mapping, lbc->bti, errStrBuf, sizeof(errStrBuf))) != NULL) + goto LUA_MAP_ERROR; + + new_mapping->map_id = 0; + if ((lbc->bti->remap_optflg & REMAP_OPTFLG_MAP_ID) != 0) { + int idx = 0; + char *c; + int ret = check_remap_option(argv, argc, REMAP_OPTFLG_MAP_ID, &idx); + if (ret & REMAP_OPTFLG_MAP_ID) { + c = strchr(argv[idx], (int) '='); + new_mapping->map_id = (unsigned int) atoi(++c); + } + } + + map_from = ats_strdup(lua_tostring(L,(startN+1))); + length = UrlWhack(map_from, &origLength); + + if ((tmp = (map_from_start = map_from)) != NULL && length > 2 && tmp[length - 1] == '/' && tmp[length - 2] == '/') { + new_mapping->unique = true; + length -= 2; + } + + new_mapping->fromURL.create(NULL); + rparse = new_mapping->fromURL.parse_no_path_component_breakdown(tmp, length); + + map_from_start[origLength] = '\0'; // Unwhack + + if (rparse != PARSE_DONE) { + errStr = "Malformed From URL"; + goto LUA_MAP_ERROR; + } + + map_to = ats_strdup(lua_tostring(L,(startN+2))); + length = UrlWhack(map_to, &origLength); + map_to_start = map_to; + tmp = map_to; + + new_mapping->toUrl.create(NULL); + rparse = new_mapping->toUrl.parse_no_path_component_breakdown(tmp, length); + map_to_start[origLength] = '\0'; // Unwhack + + if (rparse != PARSE_DONE) { + errStr = "Malformed From URL"; + goto LUA_MAP_ERROR; + } + + fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen); + if (fromScheme == NULL || fromSchemeLen == 0) { + new_mapping->fromURL.scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP); + fromScheme = new_mapping->fromURL.scheme_get(&fromSchemeLen); + new_mapping->wildcard_from_scheme = true; + } + toScheme = new_mapping->toUrl.scheme_get(&toSchemeLen); + + if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS && + fromScheme != URL_SCHEME_FILE && + fromScheme != URL_SCHEME_TUNNEL) || + (toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS && + toScheme != URL_SCHEME_TUNNEL)) { + errStr = "Only http, https, and tunnel remappings are supported"; + goto LUA_MAP_ERROR; + } + + if(lua_isstring(L,(startN+3))) { + tag = ats_strdup(lua_tostring(L,(startN+3))); + if (maptype == FORWARD_MAP_REFERER) { + int j; + new_mapping->filter_redirect_url = ats_strdup(tag); + if (!strcasecmp(tag, "") || !strcasecmp(tag, "default") || + !strcasecmp(tag, "") || !strcasecmp(tag, "default_redirect_url")) + new_mapping->default_redirect_url = true; + new_mapping->redir_chunk_list = redirect_tag_str::parse_format_redirect_url(tag); + + j = startN+4; + while(lua_isstring(L,j) && j<=nargs) j++; + for (; j > (startN + 3); j--) { + char refinfo_error_buf[1024]; + bool refinfo_error = false; + + ri = NEW(new referer_info((char *) lua_tostring(L,j), &refinfo_error, refinfo_error_buf, + sizeof(refinfo_error_buf))); + if (refinfo_error) { + snprintf(errStrBuf, sizeof(errStrBuf), "%s Incorrect Referer regular expression \"%s\" - %s", + modulePrefix, lua_tostring(L,j), refinfo_error_buf); + SignalError(errStrBuf, alarm_already); + delete ri; + ri = 0; + } + + if (ri && ri->negative) { + if (ri->any) { + new_mapping->optional_referer = true; /* referer header is optional */ + delete ri; + ri = 0; + } else { + new_mapping->negative_referer = true; /* we have negative referer in list */ + } + } + if (ri) { + ri->next = new_mapping->referer_list; + new_mapping->referer_list = ri; + } + } + } else { + new_mapping->tag = ats_strdup(lua_tostring(L,(startN+3))); + } + } + + fromHost = new_mapping->fromURL.host_get(&fromHostLen); + if (fromHost == NULL || fromHostLen <= 0) { + if (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) { + if (*map_from_start != '/') { + errStr = "Relative remappings must begin with a /"; + goto LUA_MAP_ERROR; + } else { + fromHost = ""; + fromHostLen = 0; + } + } else { + errStr = "Remap source in reverse mappings requires a hostname"; + goto LUA_MAP_ERROR; + } + } + + toHost = new_mapping->toUrl.host_get(&toHostLen); + if (toHost == NULL || toHostLen <= 0) { + errStr = "The remap destinations require a hostname"; + goto LUA_MAP_ERROR; + } + + if (unlikely(fromHostLen >= (int) sizeof(fromHost_lower_buf))) { + fromHost_lower = (fromHost_lower_ptr = (char *)ats_malloc(fromHostLen + 1)); + } else { + fromHost_lower = &fromHost_lower_buf[0]; + } + memcpy(fromHost_lower, fromHost, fromHostLen); + fromHost_lower[fromHostLen] = 0; + LowerCaseStr(fromHost_lower); + + new_mapping->fromURL.host_set(fromHost_lower, fromHostLen); + + reg_map = NULL; + if (is_cur_mapping_regex) { + reg_map = NEW(new RegexMapping); + if (!_processRegexMappingConfig(fromHost_lower, new_mapping, reg_map)) { + errStr = "Could not process regex mapping config line"; + goto LUA_MAP_ERROR; + } + Debug("url_rewrite_regex", "Configured regex rule for host [%s]", fromHost_lower); + } + + if ((maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT) && + fromScheme == URL_SCHEME_TUNNEL && (fromHost_lower[0]<'0' || fromHost_lower[0]> '9')) { + addrinfo* ai_records; // returned records. + ip_text_buffer ipb; // buffer for address string conversion. + if (0 == getaddrinfo(fromHost_lower, 0, 0, &ai_records)) { + for ( addrinfo* ai_spot = ai_records ; ai_spot ; ai_spot = ai_spot->ai_next) { + if (ats_is_ip(ai_spot->ai_addr) && + !ats_is_ip_any(ai_spot->ai_addr)) { + url_mapping *u_mapping; + + ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb); + u_mapping = NEW(new url_mapping); + u_mapping->fromURL.create(NULL); + u_mapping->fromURL.copy(&new_mapping->fromURL); + u_mapping->fromURL.host_set(ipb, strlen(ipb)); + u_mapping->toUrl.create(NULL); + u_mapping->toUrl.copy(&new_mapping->toUrl); + if (lua_isstring(L,(startN+3))) + u_mapping->tag = ats_strdup(lua_tostring(L,(startN+3))); + bool insert_result = (maptype != FORWARD_MAP_WITH_RECV_PORT) ? + TableInsert(forward_mappings.hash_lookup, u_mapping, ipb) : + TableInsert(forward_mappings_with_recv_port.hash_lookup, u_mapping, ipb); + if (!insert_result) { + errStr = "Unable to add mapping rule to lookup table"; + goto LUA_MAP_ERROR; + } + (maptype != FORWARD_MAP_WITH_RECV_PORT) ? ++num_rules_forward : ++num_rules_forward_with_recv_port; + SetHomePageRedirectFlag(u_mapping, u_mapping->toUrl); + } + } + freeaddrinfo(ai_records); + } + } + + if ((lbc->bti->remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 && (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || + maptype == FORWARD_MAP_WITH_RECV_PORT)) { + int tok_count; + if ((check_remap_option(argv, argc, REMAP_OPTFLG_PLUGIN, &tok_count) & REMAP_OPTFLG_PLUGIN) != 0) { + int plugin_found_at = 0; + int jump_to_argc = 0; + + // this loads the first plugin + if (load_remap_plugin(argv, argc, new_mapping, errStrBuf, sizeof(errStrBuf), 0, &plugin_found_at)) { + Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error"); + errStr = errStrBuf; + goto LUA_MAP_ERROR; + } + //this loads any subsequent plugins (if present) + while (plugin_found_at) { + jump_to_argc += plugin_found_at; + if (load_remap_plugin(argv, argc, new_mapping, errStrBuf, sizeof(errStrBuf), jump_to_argc, &plugin_found_at)) { + Debug("remap_plugin", "Remap plugin load error - %s", errStrBuf[0] ? errStrBuf : "Unknown error"); + errStr = errStrBuf; + goto LUA_MAP_ERROR; + } + } + } + } + + // Now add the mapping to appropriate container + add_result = false; + switch (maptype) { + case FORWARD_MAP: + case FORWARD_MAP_REFERER: + if ((add_result = _addToStore(forward_mappings, new_mapping, reg_map, fromHost_lower, + is_cur_mapping_regex, num_rules_forward)) == true) { + // @todo: is this applicable to regex mapping too? + SetHomePageRedirectFlag(new_mapping, new_mapping->toUrl); + } + break; + case REVERSE_MAP: + add_result = _addToStore(reverse_mappings, new_mapping, reg_map, fromHost_lower, + is_cur_mapping_regex, num_rules_reverse); + new_mapping->homePageRedirect = false; + break; + case PERMANENT_REDIRECT: + add_result = _addToStore(permanent_redirects, new_mapping, reg_map, fromHost_lower, + is_cur_mapping_regex, num_rules_redirect_permanent); + break; + case TEMPORARY_REDIRECT: + add_result = _addToStore(temporary_redirects, new_mapping, reg_map, fromHost_lower, + is_cur_mapping_regex, num_rules_redirect_temporary); + break; + case FORWARD_MAP_WITH_RECV_PORT: + add_result = _addToStore(forward_mappings_with_recv_port, new_mapping, reg_map, fromHost_lower, + is_cur_mapping_regex, num_rules_forward_with_recv_port); + break; + default: + // 'default' required to avoid compiler warning; unsupported map + // type would have been dealt with much before this + break; + } + if (!add_result) { + errStr = "Unable to add mapping rule to lookup table"; + goto LUA_MAP_ERROR; + } + + fromHost_lower_ptr = (char *)ats_free_null(fromHost_lower_ptr); + + errStr = NULL; + LUA_MAP_ERROR: + if(map_from) ats_free(map_from); + if(map_to) ats_free(map_to); + if(tag) ats_free(tag); + for(;argc>0;argc--) ats_free(argv[argc-1]); + if(errStr) luaL_error(L, errStr); + return 0; +} + +static int +UrlRewrite_lua_mapstuff(lua_State *L) { + struct luabuilder_crutch *lbc; + lbc = (struct luabuilder_crutch *)lua_touserdata(L, lua_upvalueindex(1)); + return lbc->urlrewrite->lua_mapstuff(L); +} + +static int +UrlRewrite_lua_index_func(lua_State *L) { + bool is_cur_mapping_regex; + bool should_domap; + mapping_type maptype = FORWARD_MAP; + const char *func; + const char *mapfunc; + struct luabuilder_crutch *lbc; + lbc = (struct luabuilder_crutch *)lua_touserdata(L,1); + func = lua_tostring(L,2); + if(!strcmp(func,"definefilter") || + !strcmp(func,"deletefilter") || + !strcmp(func,"activatefilter") || + !strcmp(func,"deactivatefilter")) { + lua_pushlightuserdata(L, lbc); + lua_pushstring(L, func); + lua_pushcclosure(L, UrlRewrite_lua_filterstuff, 2); + return 1; + } + + mapfunc = func; + should_domap = true; + is_cur_mapping_regex = (strncasecmp("regex_", mapfunc, 6) == 0); + if(is_cur_mapping_regex) mapfunc += 6; + if (!strcasecmp("reverse_map", mapfunc)) maptype = REVERSE_MAP; + else if (!strcasecmp("map", mapfunc)) maptype = FORWARD_MAP; // update late for optflg + else if (!strcasecmp("redirect", mapfunc)) maptype = PERMANENT_REDIRECT; + else if (!strcasecmp("redirect_temporary", mapfunc)) maptype = TEMPORARY_REDIRECT; + else if (!strcasecmp("map_with_referer", mapfunc)) maptype = FORWARD_MAP_REFERER; + else if (!strcasecmp("map_with_recv_port", mapfunc)) maptype = FORWARD_MAP_WITH_RECV_PORT; + else if (is_cur_mapping_regex) luaL_error(L, "ats.UrlRewite doen't have a method '%s'", mapfunc); + else should_domap = false; + + if(should_domap) { + lua_pushlightuserdata(L, lbc); + lua_pushinteger(L, maptype); + lua_pushboolean(L, is_cur_mapping_regex); + lua_pushcclosure(L, UrlRewrite_lua_mapstuff, 3); + return 1; + } + + luaL_error(L, "ats.UrlRewite doen't have a method '%s'", func); + return 0; +} + +void UrlRewrite::luaopen(lua_State *L) { + luaL_newmetatable(L, "ats.UrlRewrite"); + lua_pushcclosure(L, UrlRewrite_lua_index_func, 0); + lua_setfield(L, -2, "__index"); +} +/** + Runs lua to finiah building + + @return zero on success and non-zero on failure. + +*/ +int +UrlRewrite::loadLua(BUILD_TABLE_INFO *bti) +{ + lua_State *L = globalLuaConfig.getL(); + struct luabuilder_crutch lbc; + lbc.urlrewrite = this; + lbc.bti = bti; + lua_pushlightuserdata(L, (void *)&lbc); + luaL_getmetatable(L, "ats.UrlRewrite"); + lua_setmetatable(L, -2); + return globalLuaConfig.call(L, "config_remap", 1); +} + /** Reads the configuration file and creates a new hash table. @@ -1500,6 +2022,12 @@ UrlRewrite::BuildTable() clear_xstr_array(bti.argv, sizeof(bti.argv) / sizeof(char *)); bti.paramc = (bti.argc = 0); + if(loadLua(&bti)) { + Warning("Could not add rules via lua"); + SignalError("Could not add rules via lua", alarm_already); + return 2; + } + // Add the mapping for backdoor urls if enabled. // This needs to be before the default PAC mapping for "" // since this is more specific diff --git a/proxy/http/remap/UrlRewrite.h b/proxy/http/remap/UrlRewrite.h index a74c4af65c5..b76bb4ea584 100644 --- a/proxy/http/remap/UrlRewrite.h +++ b/proxy/http/remap/UrlRewrite.h @@ -26,6 +26,7 @@ #include "UrlMapping.h" #include "HttpTransact.h" +#include "lua.hpp" #ifdef HAVE_PCRE_PCRE_H #include @@ -74,8 +75,10 @@ class UrlRewrite void SetReverseFlag(int flag); void Print(); bool is_valid() const { return _valid; }; + int lua_mapstuff(lua_State *L); // private: + static void luaopen(lua_State *L); static const int MAX_REGEX_SUBS = 10; struct RegexMapping @@ -115,6 +118,7 @@ class UrlRewrite url_mapping *SetupPacMapping(); // manager proxy-autconfig mapping url_mapping *SetupBackdoorMapping(); void PrintStore(MappingsStore &store); + int loadLua(BUILD_TABLE_INFO *); void DestroyStore(MappingsStore &store) { diff --git a/proxy/luaConfig.cc b/proxy/luaConfig.cc new file mode 100644 index 00000000000..626b40de469 --- /dev/null +++ b/proxy/luaConfig.cc @@ -0,0 +1,340 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "libts.h" +#include "luaConfig.h" +#include "I_Layout.h" +#include "I_RecDefs.h" +#include "I_RecCore.h" +#include "P_RecCore.h" +#include "UrlRewrite.h" + +luaConfig globalLuaConfig("tsconfig"); + +static int +tsrec_newindex_func(lua_State *L) { + luaL_error(L, "cannot assign to ats.config_t, invoked instead"); + return 0; +} + +static int +tsrec_index_func(lua_State *L) { + const char *opath = NULL; + char path[1024]; + assert(lua_gettop(L) == 2); + if(!strcmp(lua_tostring(L,2),"_path")) return 0; + lua_pushvalue(L,2); + lua_rawget(L,1); + if(lua_istable(L,-1)) return 1; + lua_pop(L,1); + + lua_pushstring(L,"_path"); + lua_rawget(L,1); + if(lua_isstring(L,-1)) opath = lua_tostring(L,-1); + if(opath == NULL) snprintf(path, sizeof(path), "%s", lua_tostring(L,2)); + else snprintf(path, sizeof(path), "%s.%s", opath, lua_tostring(L,2)); + + lua_newtable(L); + lua_pushstring(L, path); + lua_setfield(L, -2, "_path"); + + luaL_getmetatable(L, "ats.config_t"); + lua_setmetatable(L, -2); + + lua_pushvalue(L,2); /* requested name */ + lua_pushvalue(L,-2); + lua_rawset(L,1); + return 1; +} + +static int +tsrec_next_record(lua_State *L) { + RecRecord *r = NULL; + int *idx, i, num_records, upidx; + upidx = lua_upvalueindex(1); + idx = (int *)lua_touserdata(L,upidx); + i = *idx; + + ink_mutex_acquire(&g_rec_config_lock); + + num_records = g_num_records; + for (; i < num_records; i++) { + r = &(g_records[i]); + if (REC_TYPE_IS_CONFIG(r->rec_type)) + break; + r = NULL; + } + if(r != NULL) rec_mutex_acquire(&(r->lock)); + ink_mutex_release(&g_rec_config_lock); + *idx = i+1; + + if(r != NULL) { + lua_newtable(L); + lua_pushstring(L, r->name); + lua_setfield(L, -2, "_path"); + + luaL_getmetatable(L, "ats.config_t"); + lua_setmetatable(L, -2); + rec_mutex_release(&(r->lock)); + return 1; + } + return 0; +} + +static int +tsrec_dispatch_method(lua_State *L, const char *bpath, const char *method) { + if(!strcmp(method, "name")) { + lua_pushstring(L, bpath); + return 1; + } + else if(!strcmp(method, "list")) { + int *idx = (int *)lua_newuserdata(L, sizeof(int)); + *idx = 0; + lua_pushcclosure(L, tsrec_next_record, 1); + return 1; + } + luaL_error(L, "unknown method call: %s", method); + return 0; +} + +static int +tsrec_call_func(lua_State *L) { + RecT rec_type; + RecDataT data_type; + const char *opath = NULL; + int nargs = lua_gettop(L); + lua_pushstring(L,"_path"); + lua_rawget(L,1); + opath = lua_tostring(L,-1); + lua_pop(L,1); + + if(lua_istable(L,2)) { + const char *bpath; + lua_pushstring(L,"_path"); + lua_rawget(L,2); + bpath = lua_tostring(L,-1); + if(bpath == NULL) return tsrec_dispatch_method(L,"",opath); + int blen = strlen(bpath); + lua_pop(L,1); + if(blen > strlen(opath) || + strncmp(bpath, opath, blen) || + opath[blen] != '.') { + luaL_error(L, "impossible method call: %s", opath + blen + 1); + } + return tsrec_dispatch_method(L,bpath,opath + blen + 1); + } + + if(RecGetRecordType(opath, &rec_type)) + luaL_error(L, "Could not find record type '%s'", opath); + if(RecGetRecordDataType(opath, &data_type)) + luaL_error(L, "Could not find record data type '%s'", opath); + if(nargs == 1) { + /* return the value */ + switch(data_type) { + case RECD_INT: + RecInt v_int; + RecGetRecordInt(opath, &v_int); + lua_pushinteger(L,v_int); + return 1; + break; + case RECD_FLOAT: + RecFloat v_float; + RecGetRecordFloat(opath, &v_float); + lua_pushnumber(L,v_float); + return 1; + break; + case RECD_STRING: + RecString v_string; + RecGetRecordString_Xmalloc(opath, &v_string); + lua_pushstring(L, v_string); + ats_free(v_string); + return 1; + break; + case RECD_COUNTER: + RecCounter v_counter; + RecGetRecordCounter(opath, &v_counter); + lua_pushinteger(L,v_counter); + return 1; + break; + default: + luaL_error(L, "Unknown type of record: %s", opath); + break; + } + return 0; + } + + switch(data_type) { + case RECD_INT: + RecSetRecordInt(opath, (RecInt)luaL_checkint(L,2)); + break; + case RECD_FLOAT: + RecSetRecordFloat(opath, (RecFloat)luaL_checknumber(L,2)); + break; + case RECD_STRING: + RecSetRecordString(opath, (const RecString)lua_tostring(L,2)); + break; + case RECD_COUNTER: + RecSetRecordCounter(opath, (RecCounter)luaL_checklong(L,2)); + break; + default: + luaL_error(L, "Unknown type of record: %s", opath); + break; + } + return 0; +} + +static int +lua_ats_log(lua_State *L) { + int type = lua_tointeger(L, lua_upvalueindex(1)); + switch(type) { + case 0: Error("%s", lua_tostring(L,1)); break; + case 1: Warning("%s", lua_tostring(L,1)); break; + case 2: Note("%s", lua_tostring(L,1)); break; + case 3: Status("%s", lua_tostring(L,1)); break; + case 4: Emergency("%s", lua_tostring(L,1)); break; + case 5: Fatal("%s", lua_tostring(L,1)); break; + case 6: Debug(lua_tostring(L,1), "%s", lua_tostring(L,2)); break; + default: + luaL_error(L, "unknown internal log type"); + break; + } + return 0; +} +void luaopen_ats(lua_State *L) { + luaL_newmetatable(L, "ats.config_t"); + lua_pushcclosure(L, tsrec_index_func, 0); + lua_setfield(L, -2, "__index"); + lua_pushcclosure(L, tsrec_newindex_func, 0); + lua_setfield(L, -2, "__newindex"); + lua_pushcclosure(L, tsrec_call_func, 0); + lua_setfield(L, -2, "__call"); + + lua_newtable(L); + lua_setglobal(L, "ats"); + lua_getglobal(L, "ats"); + + /* config */ + lua_newtable(L); + luaL_getmetatable(L, "ats.config_t"); + lua_setmetatable(L, -2); + lua_setfield(L, -2, "config"); + + lua_newtable(L); +#define mkLog(id,name) do { \ + lua_pushinteger(L,id); \ + lua_pushcclosure(L, lua_ats_log, 1); \ + lua_setfield(L, -2, #name); \ +} while(0) + mkLog(0,error); + mkLog(1,warning); + mkLog(2,note); + mkLog(3,status); + mkLog(4,emergency); + mkLog(5,fatal); + mkLog(6,debug); + lua_setfield(L, -2, "log"); +} + +void luaConfig::records() { + int rv; + lua_State *L = getL(); + lua_getglobal(L, "tsconfig"); + lua_getfield(L, -1, "config"); + if(lua_isnil(L,-1)) { + lua_pop(L,1); + return; + } + rv = lua_pcall(L, 0, 1, 0); + if(rv != 0) { + ink_error("tsconfig.config() failed: %s\n", lua_tostring(L,-1)); + } + lua_pop(L,1); +} + +void drop_lua_state_holder(void *vls) { + struct luaConfigStateHolder *state_holder = (struct luaConfigStateHolder *)vls; + lua_close(state_holder->states[0]); + lua_close(state_holder->states[1]); +} + +void luaConfig::boot() { + assert(state_holder.states[0] == NULL); + assert(state_holder.states[1] == NULL); + char system_config_directory[PATH_NAME_MAX + 1]; // Layout->sysconfdir + ink_strlcpy(system_config_directory, Layout::get()->sysconfdir, PATH_NAME_MAX); + + char config_file_path[PATH_NAME_MAX]; + config_file_path[0] = '\0'; + ink_strlcpy(config_file_path, system_config_directory, sizeof(config_file_path)); + ink_strlcat(config_file_path, "/?.lua", sizeof(config_file_path)); + setL(0, open(config_file_path, config_module)); + setL(1, open(config_file_path, config_module)); +} + +lua_State *luaConfig::open(const char *config_file_path, const char *module) { + lua_State *L = luaL_newstate(); + if(L == NULL) { + printf("[TrafficServer] failed to initialized lua.\n"); + exit(1); + } + luaL_openlibs(L); + + lua_getglobal(L, "package"); + lua_pushstring(L, config_file_path); + lua_setfield(L, -2, "path"); + lua_pop(L,1); + + luaopen_ats(L); + UrlRewrite::luaopen(L); + lua_getglobal(L,"require"); + lua_pushstring(L,module); + if(lua_pcall(L,1,1,0) != 0) { + printf("[TrafficServer] failed to run lua config '%s'\n%s", config_file_path, + lua_tostring(L,-1)); + exit(1); + } + lua_pop(L, lua_gettop(L)); + return L; +} + +int +luaConfig::call(lua_State *L, const char *method, int nargs) { + lua_getglobal(L, "tsconfig"); + lua_getfield(L, -1, method); + if(lua_isnil(L,-1)) { + lua_pop(L,2); + return -1; + } + lua_remove(L,-2); + lua_insert(L, 0 - (nargs+1)); + if(lua_pcall(L, nargs, 0, 0) != 0) { + Error("lua call(%s) failed: %s\n", method, lua_tostring(L,-1)); + return -1; + } + return 0; +} + +void luaConfigInit() { + globalLuaConfig.boot(); + globalLuaConfig.records(); +} diff --git a/proxy/luaConfig.h b/proxy/luaConfig.h new file mode 100644 index 00000000000..28e0576df24 --- /dev/null +++ b/proxy/luaConfig.h @@ -0,0 +1,92 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#if !defined (_luaConfig_h_) +#define _luaConfig_h_ + +#include "ink_apidefs.h" +#include "lua.hpp" + +///////////////////////////////////////////////////////////// +// +// class luaConfig +// +///////////////////////////////////////////////////////////// + +void luaConfigInit(); +void drop_lua_state_holder(void *); + +struct luaConfigStateHolder { +public: + luaConfigStateHolder() : active(0), uses_remaining(0) { + states[0] = states[1] = NULL; + } + lua_State *states[2]; + int active; + int64_t uses_remaining; +}; + +class luaConfig +{ +public: + luaConfig(const char *mod) : config_module(mod) { + ink_thread_key_create(&state_key, drop_lua_state_holder); + } + void boot(); + void records(); + inline lua_State *getL() { return getL(-1); } + int call(lua_State *, const char *method, int nargs); + inline int call(const char *method, int nargs) { + return call(getL(), method, nargs); + } + +private: + lua_State *open(const char *path, const char *module); + inline void setL(int which, lua_State *L) { + assert(which >= 0 && which < 2); + struct luaConfigStateHolder *state_holder; + state_holder = (struct luaConfigStateHolder *)ink_thread_getspecific(state_key); + if(state_holder == NULL) { + state_holder = new struct luaConfigStateHolder(); + ink_thread_setspecific(state_key, (void *)state_holder); + } + state_holder->states[which] = L; + } + inline lua_State *getL(int which) { + assert(which >= -1 && which < 2); + struct luaConfigStateHolder *state_holder; + state_holder = (struct luaConfigStateHolder *)ink_thread_getspecific(state_key); + if(state_holder == NULL) { + state_holder = new struct luaConfigStateHolder(); + ink_thread_setspecific(state_key, (void *)state_holder); + } + return state_holder->states[(which < 0) ? state_holder->active : which]; + } + ink_thread_key state_key; + const char *config_module; + struct luaConfigStateHolder state_holder; +}; + +extern luaConfig globalLuaConfig; + +#endif /* _luaConfig_h_ */