diff --git a/configure.ac b/configure.ac index e2354401..3a64fc83 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,11 @@ AS_IF([test "x$dns" = "xyes"], ]) ]) +SNIPROXY_WITH_LUAJIT +AS_IF([test "x$with_luajit" = "xno"], [ + SNIPROXY_WITH_LUA +]) + AC_ARG_ENABLE([rfc3339-timestamps], [AS_HELP_STRING([--enable-rfc3339-timestamps], [Enable RFC3339 timestamps])], [rfc3339_timestamps=${enableval}], [rfc3339_timestamps=no]) diff --git a/m4/sniproxy_with_lua.m4 b/m4/sniproxy_with_lua.m4 new file mode 100644 index 00000000..79bb445b --- /dev/null +++ b/m4/sniproxy_with_lua.m4 @@ -0,0 +1,36 @@ +AC_DEFUN([SNIPROXY_WITH_LUA],[ + AC_MSG_CHECKING([whether we will be linking in Lua]) + AC_ARG_WITH([lua], + [AS_HELP_STRING([--with-lua], [build Lua Bindings @<:@default=auto@:>@])], + [with_lua=$withval], + [with_lua=auto] + ) + AC_MSG_RESULT([$with_lua]) + + AS_IF([test "x$with_lua" != "xno"],[ + AS_IF([test "x$with_lua" = "xyes" -o "x$with_lua" = "xauto"], + [for LUAPC in lua5.3 lua-5.3 lua53 lua5.2 lua-5.2 lua52 lua5.1 lua-5.1 lua51 lua; do + PKG_CHECK_MODULES([LUA], $LUAPC >= 5.1, [ + AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have lua]) + with_lua=yes + ], [LUAPC=""]) # otherwise pkg_check will fail + if test "x$LUA_LIBS" != "x"; then break; fi + done + ], + [LUAPC="$with_lua" + PKG_CHECK_MODULES([LUA], $LUAPC >= 5.1, [ + AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have lua]) + with_lua=yes + ]) + ]) + AC_MSG_CHECKING([for chosen LUA]) + AS_IF([test "x$LUAPC" = "x"], [ + AS_IF([test "x$with_lua" = "xyes"], + [AC_MSG_ERROR([cannot find lua])], + [AC_MSG_RESULT([not found])] + )],[ + AC_MSG_RESULT([$LUAPC]) + ]) + ]) + AM_CONDITIONAL([LUA], [test "x$with_lua" = "xyes"]) +]) diff --git a/m4/sniproxy_with_luajit.m4 b/m4/sniproxy_with_luajit.m4 new file mode 100644 index 00000000..a448ddff --- /dev/null +++ b/m4/sniproxy_with_luajit.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([SNIPROXY_WITH_LUAJIT],[ + AC_MSG_CHECKING([whether we will be linking in LuaJIT]) + AC_ARG_WITH([luajit], + [AS_HELP_STRING([--with-luajit], [build LuaJIT bindings @<:@default=auto@:>@])], + [with_luajit=$withval], + [with_luajit=no] + ) + AC_MSG_RESULT([$with_luajit]) + + AS_IF([test "x$with_luajit" = "xyes"], [ + LUAJITPC="$with_luajit" + PKG_CHECK_MODULES([LUA], [luajit], + [AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have LuaJIT])], + [LUAJITPC=""] + ) + AS_IF([test "x$LUAJITPC" = "x"], [ + AC_MSG_ERROR([LuaJIT not found])] + ) + ]) + + AM_CONDITIONAL([LUA], [test "x$with_luajit" = "xyes"]) +]) diff --git a/sniproxy.lua b/sniproxy.lua new file mode 100644 index 00000000..7d277d22 --- /dev/null +++ b/sniproxy.lua @@ -0,0 +1,13 @@ +function preconnect(remote, name) + print('->') + print("["..remote.."]") + print("["..name.."]") + name = string.lower(name) + print("["..name.."]") + if remote == '127.0.0.1' and name == 'www.example.com' then + print('blocking') + return true + end + print('--') + return false +end diff --git a/src/Makefile.am b/src/Makefile.am index ecc1f376..353010d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = $(LIBEV_CFLAGS) $(LIBPCRE_CFLAGS) $(LIBUDNS_CFLAGS) +AM_CPPFLAGS = $(LIBEV_CFLAGS) $(LIBPCRE_CFLAGS) $(LIBUDNS_CFLAGS) $(LUA_CFLAGS) sbin_PROGRAMS = sniproxy @@ -33,4 +33,4 @@ sniproxy_SOURCES = sniproxy.c \ tls.c \ tls.h -sniproxy_LDADD = $(LIBEV_LIBS) $(LIBPCRE_LIBS) $(LIBUDNS_LIBS) +sniproxy_LDADD = $(LIBEV_LIBS) $(LIBPCRE_LIBS) $(LIBUDNS_LIBS) $(LUA_LIBS) diff --git a/src/config.c b/src/config.c index fcbee55a..08eb2932 100644 --- a/src/config.c +++ b/src/config.c @@ -39,6 +39,7 @@ struct LoggerBuilder { static int accept_username(struct Config *, char *); static int accept_pidfile(struct Config *, char *); +static int accept_luafilename(struct Config *, char *); static int end_listener_stanza(struct Config *, struct Listener *); static int end_table_stanza(struct Config *, struct Table *); static int end_backend(struct Table *, struct Backend *); @@ -151,6 +152,11 @@ static struct Keyword global_grammar[] = { (int(*)(void *, char *))accept_pidfile, NULL, NULL}, + { "luafilename", + NULL, + (int(*)(void *, char *))accept_luafilename, + NULL, + NULL}, { "resolver", (void *(*)())new_resolver_config, NULL, @@ -198,6 +204,7 @@ init_config(const char *filename, struct ev_loop *loop) { config->filename = NULL; config->user = NULL; config->pidfile = NULL; + config->luafilename = NULL; config->access_log = NULL; config->resolver.nameservers = NULL; config->resolver.search = NULL; @@ -253,6 +260,7 @@ free_config(struct Config *config, struct ev_loop *loop) { free(config->filename); free(config->user); free(config->pidfile); + free(config->luafilename); free_string_vector(config->resolver.nameservers); config->resolver.nameservers = NULL; @@ -302,6 +310,9 @@ print_config(FILE *file, struct Config *config) { if (config->pidfile) fprintf(file, "pidfile %s\n\n", config->pidfile); + if (config->luafilename) + fprintf(file, "luafilename %s\n\n", config->luafilename); + print_resolver_config(file, &config->resolver); SLIST_FOREACH(listener, &config->listeners, entries) { @@ -335,6 +346,17 @@ accept_pidfile(struct Config *config, char *pidfile) { return 1; } +static int +accept_luafilename(struct Config *config, char *luafilename) { + config->luafilename = strdup(luafilename); + if (config->luafilename == NULL) { + err("%s: strdup", __func__); + return -1; + } + + return 1; +} + static int end_listener_stanza(struct Config *config, struct Listener *listener) { if (valid_listener(listener) <= 0) { diff --git a/src/config.h b/src/config.h index 64d19c2d..2473123f 100644 --- a/src/config.h +++ b/src/config.h @@ -34,6 +34,7 @@ struct Config { char *filename; char *user; char *pidfile; + char *luafilename; struct ResolverConfig { char **nameservers; char **search; diff --git a/src/connection.c b/src/connection.c index 2a93899f..137e5512 100644 --- a/src/connection.c +++ b/src/connection.c @@ -24,6 +24,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include @@ -43,6 +44,12 @@ #include #endif +#ifdef HAVE_LUA +#include +#include +#include +#endif + #include "connection.h" #include "resolv.h" #include "address.h" @@ -205,6 +212,86 @@ server_socket_open(const struct Connection *con) { con->state == CLIENT_CLOSED; } +static bool is_mapped_ipv4(const struct sockaddr_in6* addr) { + if (addr->sin6_family != AF_INET6) { + return false; + } + + const unsigned char* ptr = (const unsigned char*) &addr->sin6_addr.s6_addr; + for (size_t idx = 0; idx < 10; idx++) { + if (ptr[idx] != 0) { + return false; + } + } + + if (ptr[10] != 0xff) { + return false; + } + + if (ptr[11] != 0xff) { + return false; + } + + return true; +} + +static void map_to_v4(const struct sockaddr_in6* src, struct sockaddr_in* dst) { + const unsigned char* ptr = (const unsigned char*) &src->sin6_addr.s6_addr; + dst->sin_family = AF_INET; + dst->sin_port = src->sin6_port; + + ptr += (sizeof(src->sin6_addr.s6_addr) - sizeof(dst->sin_addr.s_addr)); + memcpy(&dst->sin_addr.s_addr, ptr, sizeof(dst->sin_addr.s_addr)); +} + +#ifdef HAVE_LUA +lua_State *lua_state; + +static void apply_lua_policy(struct Connection *con) { + if(lua_state) { + const struct sockaddr_storage *saddr = &(con->client.addr); + struct sockaddr_storage unmapped; + char addr[INET6_ADDRSTRLEN]; + + addr[0]='\0'; + if (saddr->ss_family == AF_INET) { + inet_ntop(saddr->ss_family, &((struct sockaddr_in *) saddr)->sin_addr, addr, INET_ADDRSTRLEN); + } else if (saddr->ss_family == AF_INET6) { + if (is_mapped_ipv4((struct sockaddr_in6 *) saddr)) { + map_to_v4((struct sockaddr_in6 *) saddr, (struct sockaddr_in *) &unmapped); + saddr = &unmapped; + inet_ntop(saddr->ss_family, &((struct sockaddr_in *) saddr)->sin_addr, addr, INET_ADDRSTRLEN); + } + else { + inet_ntop(saddr->ss_family, &((struct sockaddr_in6 *) saddr)->sin6_addr, addr, INET6_ADDRSTRLEN); + } + } + + lua_getglobal(lua_state, "preconnect"); + if(!lua_isfunction(lua_state, -1)) { + fprintf(stderr, "no lua function\n"); + lua_pop(lua_state, 1); + return; + } + + lua_pushstring(lua_state, addr); + lua_pushlstring(lua_state, con->hostname, con->hostname != NULL ? con->hostname_len : 0); + + if(lua_pcall(lua_state, 2, 1, 0)) { + fprintf(stderr, "lua hook error: %s\n", lua_tostring(lua_state, -1)); + lua_pop(lua_state, 1); + return; + } + + int block = lua_toboolean(lua_state, 1); + lua_pop(lua_state, 1); + fprintf(stderr, "block=%d\n", block); + if(block) + abort_connection(con); + } +} +#endif + /* * Main client callback: this is used by both the client and server watchers * @@ -254,8 +341,12 @@ connection_cb(struct ev_loop *loop, struct ev_io *w, int revents) { /* Handle any state specific logic */ if (is_client && con->state == ACCEPTED) parse_client_request(con); - if (is_client && con->state == PARSED) + if (is_client && con->state == PARSED) { +#ifdef HAVE_LUA + apply_lua_policy(con); +#endif resolve_server_address(con, loop); + } if (is_client && con->state == RESOLVED) initiate_server_connect(con, loop); @@ -370,6 +461,7 @@ parse_client_request(struct Connection *con) { con->hostname = hostname; con->hostname_len = result; + fprintf(stderr, "got hostname [%s] len [%d]\n", hostname != NULL ? hostname : "(null)", result); con->state = PARSED; } diff --git a/src/sniproxy.c b/src/sniproxy.c index 86d144d4..f0d871bb 100644 --- a/src/sniproxy.c +++ b/src/sniproxy.c @@ -45,6 +45,11 @@ #include "resolv.h" #include "logger.h" +#ifdef HAVE_LUA +#include +#include +#include +#endif static void usage(); static void daemonize(void); @@ -63,6 +68,9 @@ static struct ev_signal sigusr1_watcher; static struct ev_signal sigint_watcher; static struct ev_signal sigterm_watcher; +#ifdef HAVE_LUA +extern lua_State *lua_state; +#endif int main(int argc, char **argv) { @@ -121,6 +129,21 @@ main(int argc, char **argv) { /* Drop permissions only when we can */ drop_perms(config->user ? config->user : default_username); + if(config->luafilename) { +#ifdef HAVE_LUA + lua_state = luaL_newstate(); + luaL_openlibs(lua_state); + + if(luaL_dofile(lua_state, config->luafilename)) { + fprintf(stderr, "lua dofile error: %s\n", lua_isstring(lua_state, -1) ? lua_tostring(lua_state, -1) : "unknown error"); + return EXIT_FAILURE; + } +#else + fprintf(stderr, "luafilename specified but Lua support not compiled in\n"); + return EXIT_FAILURE; +#endif + } + ev_signal_init(&sighup_watcher, signal_cb, SIGHUP); ev_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1); ev_signal_init(&sigint_watcher, signal_cb, SIGINT); diff --git a/tests/Makefile.am b/tests/Makefile.am index 53849952..ad4bcce9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I$(top_srcdir)/src -g $(LIBEV_CFLAGS) $(LIBPCRE_CFLAGS) $(LIBUDNS_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/src -g $(LIBEV_CFLAGS) $(LIBPCRE_CFLAGS) $(LIBUDNS_CFLAGS) $(LUA_CFLAGS) TESTS = http_test \ tls_test \ @@ -46,7 +46,7 @@ binder_test_SOURCES = binder_test.c \ buffer_test_SOURCES = buffer_test.c \ ../src/buffer.c -buffer_test_LDADD = $(LIBEV_LIBS) +buffer_test_LDADD = $(LIBEV_LIBS) $(LUA_LIBS) address_test_SOURCES = address_test.c \ ../src/address.c @@ -71,14 +71,14 @@ config_test_SOURCES = config_test.c \ ../src/tls.c \ ../src/http.c -config_test_LDADD = $(LIBEV_LIBS) $(LIBPCRE_LIBS) $(LIBUDNS_LIBS) +config_test_LDADD = $(LIBEV_LIBS) $(LIBPCRE_LIBS) $(LIBUDNS_LIBS) $(LUA_LIBS) resolv_test_SOURCES = resolv_test.c \ ../src/resolv.c \ ../src/address.c \ ../src/logger.c -resolv_test_LDADD = $(LIBEV_LIBS) $(LIBUDNS_LIBS) +resolv_test_LDADD = $(LIBEV_LIBS) $(LIBUDNS_LIBS) $(LUA_LIBS) table_test_SOURCES = table_test.c \ ../src/backend.c \ @@ -86,4 +86,4 @@ table_test_SOURCES = table_test.c \ ../src/address.c \ ../src/logger.c -table_test_LDADD = $(LIBPCRE_LIBS) +table_test_LDADD = $(LIBPCRE_LIBS) $(LUA_LIBS)