diff --git a/src/inet.c b/src/inet.c index 69d32e6..9be2da9 100644 --- a/src/inet.c +++ b/src/inet.c @@ -5,11 +5,27 @@ #include #include +#ifndef _WIN32 + #include + #include + #include + #include + #include +#endif + #include "lua.h" #include "lauxlib.h" #include "inet.h" +typedef union { + struct sockaddr_storage sas; + struct sockaddr sa; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; +} SA_union; + + /*=========================================================================*\ * Internal function prototypes. \*=========================================================================*/ @@ -17,11 +33,12 @@ static int inet_global_toip(lua_State *L); static int inet_global_getaddrinfo(lua_State *L); static int inet_global_tohostname(lua_State *L); static int inet_global_getnameinfo(lua_State *L); +static int inet_global_local_addresses(lua_State *L); static void inet_pushresolved(lua_State *L, struct hostent *hp); static int inet_global_gethostname(lua_State *L); /* DNS functions */ -static luaL_Reg func[] = { +static luaL_Reg dns[] = { { "toip", inet_global_toip}, { "getaddrinfo", inet_global_getaddrinfo}, { "tohostname", inet_global_tohostname}, @@ -30,6 +47,12 @@ static luaL_Reg func[] = { { NULL, NULL} }; +/* global functions */ +static luaL_Reg func[] = { + { "local_addresses", inet_global_local_addresses}, + { NULL, NULL} +}; + /*=========================================================================*\ * Exported functions \*=========================================================================*/ @@ -40,8 +63,11 @@ int inet_open(lua_State *L) { lua_pushstring(L, "dns"); lua_newtable(L); - luaL_openlib(L, NULL, func, 0); + luaL_openlib(L, NULL, dns, 0); lua_settable(L, -3); + + /* Export as global functions */ + luaL_openlib(L, NULL, func, 0); return 0; } @@ -221,6 +247,79 @@ static int inet_global_gethostname(lua_State *L) } } +/*-------------------------------------------------------------------------*\ +* Enumerate all locally configured IP addresses +\*-------------------------------------------------------------------------*/ + +const char * const type_strings[] = { + "both", + "ipv4", + "ipv6", + NULL +}; + +int inet_global_local_addresses(lua_State *L) +{ + /* Link-local IPv4 addresses; see RFC 3927 and RFC 5735 */ + const long ip4_linklocal = htonl(0xa9fe0000); /* 169.254.0.0 */ + const long ip4_mask = htonl(0xffff0000); +#ifndef _WIN32 + struct ifaddrs *addr = NULL, *a; + int n = 1; +#endif + int type = luaL_checkoption(L, 1, "both", type_strings); + const char link_local = lua_toboolean(L, 2); /* defaults to 0 (false) */ + const char ipv4 = (type == 0 || type == 1); + const char ipv6 = (type == 0 || type == 2); + +#ifndef _WIN32 + if (getifaddrs(&addr) < 0) { + lua_pushnil(L); + lua_pushfstring(L, "getifaddrs failed (%d): %s", errno, + strerror(errno)); + return 2; + } + + lua_newtable(L); + + for (a = addr; a; a = a->ifa_next) { + int family; + char ipaddr[INET6_ADDRSTRLEN]; + const char *tmp = NULL; + + if (a->ifa_addr == NULL || a->ifa_flags & IFF_LOOPBACK) + continue; + + family = a->ifa_addr->sa_family; + + if (ipv4 && family == AF_INET) { + struct sockaddr_in *sa = (struct sockaddr_in *)a->ifa_addr; + if (!link_local && + ((sa->sin_addr.s_addr & ip4_mask) == ip4_linklocal)) + continue; + tmp = inet_ntop(family, &sa->sin_addr, ipaddr, sizeof(ipaddr)); + } else if (ipv6 && family == AF_INET6) { + struct sockaddr_in6 *sa = (struct sockaddr_in6 *)a->ifa_addr; + if (!link_local && IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) + continue; + if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr) || IN6_IS_ADDR_V4COMPAT(&sa->sin6_addr)) + continue; + tmp = inet_ntop(family, &sa->sin6_addr, ipaddr, sizeof(ipaddr)); + } + + if (tmp != NULL) { + lua_pushstring(L, tmp); + lua_rawseti(L, -2, n++); + } + /* TODO: Error reporting? */ + } + + freeifaddrs(addr); + + return 1; +#endif +} + /*=========================================================================*\