diff options
Diffstat (limited to 'libs/luci-lib-nixio/src/address.c')
-rw-r--r-- | libs/luci-lib-nixio/src/address.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/libs/luci-lib-nixio/src/address.c b/libs/luci-lib-nixio/src/address.c new file mode 100644 index 0000000000..4fd557d6a7 --- /dev/null +++ b/libs/luci-lib-nixio/src/address.c @@ -0,0 +1,565 @@ +/* + * nixio - Linux I/O library for lua + * + * Copyright (C) 2009 Steven Barth <steven@midlink.org> + * + * Licensed 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 "nixio.h" +#include <sys/types.h> +#include <sys/param.h> +#include <errno.h> +#include <string.h> + +#ifdef __linux__ + +#include <signal.h> +#include <setjmp.h> +#include <unistd.h> + +/* setjmp() / longjmp() stuff */ +static jmp_buf nixio__jump_alarm; +static void nixio__handle_alarm(int sig) { longjmp(nixio__jump_alarm, 1); } + +#include <linux/netdevice.h> + +/* struct net_device_stats is buggy on amd64, redefine it */ +struct nixio__nds { + uint32_t rx_packets; + uint32_t tx_packets; + uint32_t rx_bytes; + uint32_t tx_bytes; + uint32_t rx_errors; + uint32_t tx_errors; + uint32_t rx_dropped; + uint32_t tx_dropped; + uint32_t multicast; + uint32_t collisions; + + uint32_t rx_length_errors; + uint32_t rx_over_errors; + uint32_t rx_crc_errors; + uint32_t rx_frame_errors; + uint32_t rx_fifo_errors; + uint32_t rx_missed_errors; + + uint32_t tx_aborted_errors; + uint32_t tx_carrier_errors; + uint32_t tx_fifo_errors; + uint32_t tx_heartbeat_errors; + uint32_t tx_window_errors; + + uint32_t rx_compressed; + uint32_t tx_compressed; +}; +#endif + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif + +/** + * address pushing helper + */ +int nixio__addr_parse(nixio_addr *addr, struct sockaddr *saddr) { + void *baddr; + + addr->family = saddr->sa_family; + if (saddr->sa_family == AF_INET) { + struct sockaddr_in *inetaddr = (struct sockaddr_in*)saddr; + addr->port = ntohs(inetaddr->sin_port); + baddr = &inetaddr->sin_addr; + } else if (saddr->sa_family == AF_INET6) { + struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)saddr; + addr->port = ntohs(inet6addr->sin6_port); + baddr = &inet6addr->sin6_addr; +#ifdef AF_PACKET + } else if (saddr->sa_family == AF_PACKET) { + struct sockaddr_ll *etheradddr = (struct sockaddr_ll*)saddr; + addr->prefix = etheradddr->sll_hatype; + addr->port = etheradddr->sll_ifindex; + char *c = addr->host; + for (size_t i = 0; i < etheradddr->sll_halen; i++) { + *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0xf0) >> 4]; + *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0x0f)]; + *c++ = ':'; + } + *(c-1) = 0; + return 0; +#endif + } else { + errno = EAFNOSUPPORT; + return -1; + } + + if (!inet_ntop(saddr->sa_family, baddr, addr->host, sizeof(addr->host))) { + return -1; + } + + return 0; +} + +/** + * address pulling helper + */ +int nixio__addr_write(nixio_addr *addr, struct sockaddr *saddr) { + if (addr->family == AF_UNSPEC) { + if (strchr(addr->host, ':')) { + addr->family = AF_INET6; + } else { + addr->family = AF_INET; + } + } + if (addr->family == AF_INET) { + struct sockaddr_in *inetaddr = (struct sockaddr_in *)saddr; + memset(inetaddr, 0, sizeof(struct sockaddr_in)); + + if (inet_pton(AF_INET, addr->host, &inetaddr->sin_addr) < 1) { + return -1; + } + + inetaddr->sin_family = AF_INET; + inetaddr->sin_port = htons((uint16_t)addr->port); + return 0; + } else if (addr->family == AF_INET6) { + struct sockaddr_in6 *inet6addr = (struct sockaddr_in6 *)saddr; + memset(inet6addr, 0, sizeof(struct sockaddr_in6)); + + if (inet_pton(AF_INET6, addr->host, &inet6addr->sin6_addr) < 1) { + return -1; + } + + inet6addr->sin6_family = AF_INET6; + inet6addr->sin6_port = htons((uint16_t)addr->port); + return 0; + } else { + errno = EAFNOSUPPORT; + return -1; + } +} + +/** + * netmask to prefix helper + */ +int nixio__addr_prefix(struct sockaddr *saddr) { + int prefix = 0; + size_t len; + uint8_t *addr; + + if (saddr->sa_family == AF_INET) { + addr = (uint8_t*)(&((struct sockaddr_in*)saddr)->sin_addr); + len = 4; + } else if (saddr->sa_family == AF_INET6) { + addr = (uint8_t*)(&((struct sockaddr_in6*)saddr)->sin6_addr); + len = 16; + } else { + errno = EAFNOSUPPORT; + return -1; + } + + for (size_t i = 0; i < len; i++) { + if (addr[i] == 0xff) { + prefix += 8; + } else if (addr[i] == 0x00) { + break; + } else { + for (uint8_t c = addr[i]; c; c <<= 1) { + prefix++; + } + } + } + + return prefix; +} + +/** + * getaddrinfo(host, family, port) + */ +static int nixio_getaddrinfo(lua_State *L) { + const char *host = NULL; + if (!lua_isnoneornil(L, 1)) { + host = luaL_checklstring(L, 1, NULL); + } + const char *family = luaL_optlstring(L, 2, "any", NULL); + const char *port = lua_tolstring(L, 3, NULL); + + struct addrinfo hints, *result, *rp; + memset(&hints, 0, sizeof(hints)); + + if (!strcmp(family, "any")) { + hints.ai_family = AF_UNSPEC; + } else if (!strcmp(family, "inet")) { + hints.ai_family = AF_INET; + } else if (!strcmp(family, "inet6")) { + hints.ai_family = AF_INET6; + } else { + return luaL_argerror(L, 2, "supported values: any, inet, inet6"); + } + + hints.ai_socktype = 0; + hints.ai_protocol = 0; + + int aistat = getaddrinfo(host, port, &hints, &result); + if (aistat) { + lua_pushnil(L); + lua_pushinteger(L, aistat); + lua_pushstring(L, gai_strerror(aistat)); + return 3; + } + + /* create socket object */ + lua_newtable(L); + int i = 1; + + for (rp = result; rp != NULL; rp = rp->ai_next) { + /* avoid duplicate results */ +#ifndef __WINNT__ + if (!port && rp->ai_socktype != SOCK_STREAM) { + continue; + } +#endif + + if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) { + lua_createtable(L, 0, port ? 4 : 2); + if (rp->ai_family == AF_INET) { + lua_pushliteral(L, "inet"); + } else if (rp->ai_family == AF_INET6) { + lua_pushliteral(L, "inet6"); + } + lua_setfield(L, -2, "family"); + + if (port) { + switch (rp->ai_socktype) { + case SOCK_STREAM: + lua_pushliteral(L, "stream"); + break; + case SOCK_DGRAM: + lua_pushliteral(L, "dgram"); + break; + case SOCK_RAW: + lua_pushliteral(L, "raw"); + break; + default: + lua_pushnil(L); + break; + } + lua_setfield(L, -2, "socktype"); + } + + nixio_addr addr; + if (nixio__addr_parse(&addr, rp->ai_addr)) { + freeaddrinfo(result); + return nixio__perror_s(L); + } + + if (port) { + lua_pushinteger(L, addr.port); + lua_setfield(L, -2, "port"); + } + + lua_pushstring(L, addr.host); + lua_setfield(L, -2, "address"); + lua_rawseti(L, -2, i++); + } + } + + freeaddrinfo(result); + + return 1; +} + +/** + * getnameinfo(address, family[, timeout]) + */ +static int nixio_getnameinfo(lua_State *L) { + const char *ip = luaL_checkstring(L, 1); + const char *family = luaL_optstring(L, 2, NULL); + +#ifdef __linux__ + struct sigaction sa_new, sa_old; + int timeout = luaL_optnumber(L, 3, 0); + if (timeout > 0 && timeout < 1000) + { + sa_new.sa_handler = nixio__handle_alarm; + sa_new.sa_flags = 0; + sigemptyset(&sa_new.sa_mask); + sigaction(SIGALRM, &sa_new, &sa_old); + + /* user timeout exceeded */ + if (setjmp(nixio__jump_alarm)) + { + sigaction(SIGALRM, &sa_old, NULL); + + lua_pushnil(L); + lua_pushinteger(L, EAI_AGAIN); + lua_pushstring(L, gai_strerror(EAI_AGAIN)); + + return 3; + } + + ualarm(timeout * 1000, 0); + } +#endif + + char host[NI_MAXHOST]; + + struct sockaddr_storage saddr; + nixio_addr addr; + memset(&addr, 0, sizeof(addr)); + strncpy(addr.host, ip, sizeof(addr.host) - 1); + + if (!family) { + addr.family = AF_UNSPEC; + } else if (!strcmp(family, "inet")) { + addr.family = AF_INET; + } else if (!strcmp(family, "inet6")) { + addr.family = AF_INET6; + } else { + return luaL_argerror(L, 2, "supported values: inet, inet6"); + } + + nixio__addr_write(&addr, (struct sockaddr *)&saddr); + + int res = getnameinfo((struct sockaddr *)&saddr, sizeof(saddr), + host, sizeof(host), NULL, 0, NI_NAMEREQD); + +#ifdef __linux__ + if (timeout > 0 && timeout < 1000) + { + ualarm(0, 0); + sigaction(SIGALRM, &sa_old, NULL); + } +#endif + + if (res) { + lua_pushnil(L); + lua_pushinteger(L, res); + lua_pushstring(L, gai_strerror(res)); + return 3; + } else { + lua_pushstring(L, host); + return 1; + } +} + +/** + * getsockname() + */ +static int nixio_sock_getsockname(lua_State *L) { + int sockfd = nixio__checksockfd(L); + struct sockaddr_storage saddr; + socklen_t addrlen = sizeof(saddr); + nixio_addr addr; + + if (getsockname(sockfd, (struct sockaddr*)&saddr, &addrlen) || + nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) { + return nixio__perror_s(L); + } + + lua_pushstring(L, addr.host); + lua_pushinteger(L, addr.port); + return 2; +} + +/** + * getpeername() + */ +static int nixio_sock_getpeername(lua_State *L) { + int sockfd = nixio__checksockfd(L); + struct sockaddr_storage saddr; + socklen_t addrlen = sizeof(saddr); + nixio_addr addr; + + if (getpeername(sockfd, (struct sockaddr*)&saddr, &addrlen) || + nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) { + return nixio__perror_s(L); + } + + lua_pushstring(L, addr.host); + lua_pushinteger(L, addr.port); + return 2; +} + +#if defined(__linux__) || defined(BSD) +#ifdef BSD +#include <net/if.h> +#endif +#include <ifaddrs.h> + +static int nixio_getifaddrs(lua_State *L) { + nixio_addr addr; + struct ifaddrs *ifaddr, *c; + if (getifaddrs(&ifaddr) == -1) { + return nixio__perror(L); + } + + lua_newtable(L); + unsigned int i = 1; + + for (c = ifaddr; c; c = c->ifa_next) { + lua_newtable(L); + + lua_pushstring(L, c->ifa_name); + lua_setfield(L, -2, "name"); + + lua_createtable(L, 0, 7); + lua_pushboolean(L, c->ifa_flags & IFF_UP); + lua_setfield(L, -2, "up"); + + lua_pushboolean(L, c->ifa_flags & IFF_BROADCAST); + lua_setfield(L, -2, "broadcast"); + + lua_pushboolean(L, c->ifa_flags & IFF_LOOPBACK); + lua_setfield(L, -2, "loopback"); + + lua_pushboolean(L, c->ifa_flags & IFF_POINTOPOINT); + lua_setfield(L, -2, "pointtopoint"); + + lua_pushboolean(L, c->ifa_flags & IFF_NOARP); + lua_setfield(L, -2, "noarp"); + + lua_pushboolean(L, c->ifa_flags & IFF_PROMISC); + lua_setfield(L, -2, "promisc"); + + lua_pushboolean(L, c->ifa_flags & IFF_MULTICAST); + lua_setfield(L, -2, "multicast"); + lua_setfield(L, -2, "flags"); + + if (c->ifa_addr) { + if (!nixio__addr_parse(&addr, c->ifa_addr)) { + lua_pushstring(L, addr.host); + lua_setfield(L, -2, "addr"); + } + + if (c->ifa_addr->sa_family == AF_INET) { + lua_pushliteral(L, "inet"); + } else if (c->ifa_addr->sa_family == AF_INET6) { + lua_pushliteral(L, "inet6"); +#ifdef AF_PACKET + } else if (c->ifa_addr->sa_family == AF_PACKET) { + lua_pushliteral(L, "packet"); +#endif + } else { + lua_pushliteral(L, "unknown"); + } + lua_setfield(L, -2, "family"); + +#ifdef __linux__ + if (c->ifa_addr->sa_family == AF_PACKET) { + lua_pushinteger(L, addr.port); + lua_setfield(L, -2, "ifindex"); + + lua_pushinteger(L, addr.prefix); + lua_setfield(L, -2, "hatype"); + } +#endif + } + +#ifdef __linux__ + if (c->ifa_data && (!c->ifa_addr + || c->ifa_addr->sa_family == AF_PACKET)) { + if (!c->ifa_addr) { + lua_pushliteral(L, "packet"); + lua_setfield(L, -2, "family"); + } + + lua_createtable(L, 0, 10); + struct nixio__nds *stats = c->ifa_data; + + lua_pushnumber(L, stats->rx_packets); + lua_setfield(L, -2, "rx_packets"); + + lua_pushnumber(L, stats->tx_packets); + lua_setfield(L, -2, "tx_packets"); + + lua_pushnumber(L, stats->rx_bytes); + lua_setfield(L, -2, "rx_bytes"); + + lua_pushnumber(L, stats->tx_bytes); + lua_setfield(L, -2, "tx_bytes"); + + lua_pushnumber(L, stats->rx_errors); + lua_setfield(L, -2, "rx_errors"); + + lua_pushnumber(L, stats->tx_errors); + lua_setfield(L, -2, "tx_errors"); + + lua_pushnumber(L, stats->rx_dropped); + lua_setfield(L, -2, "rx_dropped"); + + lua_pushnumber(L, stats->tx_dropped); + lua_setfield(L, -2, "tx_dropped"); + + lua_pushnumber(L, stats->multicast); + lua_setfield(L, -2, "multicast"); + + lua_pushnumber(L, stats->collisions); + lua_setfield(L, -2, "collisions"); + } else { + lua_newtable(L); + } + lua_setfield(L, -2, "data"); +#endif + + if (c->ifa_netmask && !nixio__addr_parse(&addr, c->ifa_netmask)) { + lua_pushstring(L, addr.host); + lua_setfield(L, -2, "netmask"); + + lua_pushinteger(L, nixio__addr_prefix(c->ifa_netmask)); + lua_setfield(L, -2, "prefix"); + } + + if (c->ifa_broadaddr && !nixio__addr_parse(&addr, c->ifa_broadaddr)) { + lua_pushstring(L, addr.host); + lua_setfield(L, -2, "broadaddr"); + } + + if (c->ifa_dstaddr && !nixio__addr_parse(&addr, c->ifa_dstaddr)) { + lua_pushstring(L, addr.host); + lua_setfield(L, -2, "dstaddr"); + } + + lua_rawseti(L, -2, i++); + } + + freeifaddrs(ifaddr); + return 1; +} +#endif + + +/* module table */ +static const luaL_reg R[] = { +#if defined(__linux__) || defined(BSD) + {"getifaddrs", nixio_getifaddrs}, +#endif + {"getaddrinfo", nixio_getaddrinfo}, + {"getnameinfo", nixio_getnameinfo}, + {NULL, NULL} +}; + +/* object table */ +static const luaL_reg M[] = { + {"getsockname", nixio_sock_getsockname}, + {"getpeername", nixio_sock_getpeername}, + {NULL, NULL} +}; + +void nixio_open_address(lua_State *L) { + luaL_register(L, NULL, R); + + lua_pushvalue(L, -2); + luaL_register(L, NULL, M); + lua_pop(L, 1); +} |