391 lines
11 KiB
C
391 lines
11 KiB
C
|
/*
|
||
|
* 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"
|
||
|
|
||
|
#ifndef __WINNT__
|
||
|
#include <net/if.h>
|
||
|
#endif
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <string.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#ifndef IPV6_ADD_MEMBERSHIP
|
||
|
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
||
|
#endif
|
||
|
|
||
|
#ifndef IPV6_DROP_MEMBERSHIP
|
||
|
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
|
||
|
#endif
|
||
|
|
||
|
static int nixio_sock_fileno(lua_State *L) {
|
||
|
lua_pushinteger(L, nixio__checkfd(L, 1));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setblocking()
|
||
|
*/
|
||
|
static int nixio_sock_setblocking(lua_State *L) {
|
||
|
int fd = nixio__checkfd(L, 1);
|
||
|
luaL_checkany(L, 2);
|
||
|
int set = lua_toboolean(L, 2);
|
||
|
|
||
|
#ifndef __WINNT__
|
||
|
|
||
|
int flags = fcntl(fd, F_GETFL);
|
||
|
|
||
|
if (flags == -1) {
|
||
|
return nixio__perror(L);
|
||
|
}
|
||
|
|
||
|
if (!set) {
|
||
|
flags |= O_NONBLOCK;
|
||
|
} else {
|
||
|
flags &= ~O_NONBLOCK;
|
||
|
}
|
||
|
|
||
|
return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
|
||
|
|
||
|
#else /* __WINNT__ */
|
||
|
|
||
|
lua_getmetatable(L, 1);
|
||
|
luaL_getmetatable(L, NIXIO_META);
|
||
|
if (lua_equal(L, -1, -2)) { /* Socket */
|
||
|
unsigned long val = !set;
|
||
|
return nixio__pstatus_s(L, !ioctlsocket(fd, FIONBIO, &val));
|
||
|
} else { /* File */
|
||
|
WSASetLastError(WSAENOTSOCK);
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
#endif /* __WINNT__ */
|
||
|
}
|
||
|
|
||
|
static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
int value;
|
||
|
socklen_t optlen = sizeof(value);
|
||
|
if (!set) {
|
||
|
if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
|
||
|
lua_pushinteger(L, value);
|
||
|
return 1;
|
||
|
}
|
||
|
} else {
|
||
|
value = luaL_checkinteger(L, set);
|
||
|
if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
struct linger value;
|
||
|
socklen_t optlen = sizeof(value);
|
||
|
if (!set) {
|
||
|
if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
|
||
|
lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
|
||
|
return 1;
|
||
|
}
|
||
|
} else {
|
||
|
value.l_linger = luaL_checkinteger(L, set);
|
||
|
value.l_onoff = value.l_linger ? 1 : 0;
|
||
|
if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
struct timeval value;
|
||
|
socklen_t optlen = sizeof(value);
|
||
|
if (!set) {
|
||
|
if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
|
||
|
lua_pushinteger(L, value.tv_sec);
|
||
|
lua_pushinteger(L, value.tv_usec);
|
||
|
return 2;
|
||
|
}
|
||
|
} else {
|
||
|
value.tv_sec = luaL_checkinteger(L, set);
|
||
|
value.tv_usec = luaL_optinteger(L, set + 1, 0);
|
||
|
if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
#ifdef SO_BINDTODEVICE
|
||
|
|
||
|
static int nixio__gso_b(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
if (!set) {
|
||
|
socklen_t optlen = IFNAMSIZ;
|
||
|
char ifname[IFNAMSIZ];
|
||
|
if (!getsockopt(fd, level, opt, (char *)ifname, &optlen)) {
|
||
|
lua_pushlstring(L, ifname, optlen);
|
||
|
return 1;
|
||
|
}
|
||
|
} else {
|
||
|
size_t valuelen;
|
||
|
const char *value = luaL_checklstring(L, set, &valuelen);
|
||
|
luaL_argcheck(L, valuelen <= IFNAMSIZ, set, "invalid interface name");
|
||
|
if (!setsockopt(fd, level, opt, (char *)value, valuelen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
#endif /* SO_BINDTODEVICE */
|
||
|
|
||
|
static int nixio__gso_mreq4(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
struct ip_mreq value;
|
||
|
socklen_t optlen = sizeof(value);
|
||
|
if (!set) {
|
||
|
char buf[INET_ADDRSTRLEN];
|
||
|
if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
|
||
|
if (!inet_ntop(AF_INET, &value.imr_multiaddr, buf, sizeof(buf))) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
lua_pushstring(L, buf);
|
||
|
if (!inet_ntop(AF_INET, &value.imr_interface, buf, sizeof(buf))) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
lua_pushstring(L, buf);
|
||
|
return 2;
|
||
|
}
|
||
|
} else {
|
||
|
const char *maddr = luaL_checkstring(L, set);
|
||
|
const char *iface = luaL_optstring(L, set + 1, "0.0.0.0");
|
||
|
if (inet_pton(AF_INET, maddr, &value.imr_multiaddr) < 1) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
if (inet_pton(AF_INET, iface, &value.imr_interface) < 1) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
static int nixio__gso_mreq6(lua_State *L, int fd, int level, int opt, int set) {
|
||
|
struct ipv6_mreq val;
|
||
|
socklen_t optlen = sizeof(val);
|
||
|
if (!set) {
|
||
|
char buf[INET_ADDRSTRLEN];
|
||
|
if (!getsockopt(fd, level, opt, (char *)&val, &optlen)) {
|
||
|
if (!inet_ntop(AF_INET6, &val.ipv6mr_multiaddr, buf, sizeof(buf))) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
lua_pushstring(L, buf);
|
||
|
lua_pushinteger(L, val.ipv6mr_interface);
|
||
|
return 2;
|
||
|
}
|
||
|
} else {
|
||
|
const char *maddr = luaL_checkstring(L, set);
|
||
|
if (inet_pton(AF_INET6, maddr, &val.ipv6mr_multiaddr) < 1) {
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
val.ipv6mr_interface = luaL_optlong(L, set + 1, 0);
|
||
|
if (!setsockopt(fd, level, opt, (char *)&val, optlen)) {
|
||
|
lua_pushboolean(L, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return nixio__perror_s(L);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* get/setsockopt() helper
|
||
|
*/
|
||
|
static int nixio__getsetsockopt(lua_State *L, int set) {
|
||
|
nixio_sock *sock = nixio__checksock(L);
|
||
|
const char *level = luaL_optlstring(L, 2, "", NULL);
|
||
|
const char *option = luaL_optlstring(L, 3, "", NULL);
|
||
|
set = (set) ? 4 : 0;
|
||
|
|
||
|
if (!strcmp(level, "socket")) {
|
||
|
if (!strcmp(option, "keepalive")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
|
||
|
} else if (!strcmp(option, "reuseaddr")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
|
||
|
} else if (!strcmp(option, "rcvbuf")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
|
||
|
} else if (!strcmp(option, "sndbuf")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
|
||
|
} else if (!strcmp(option, "priority")) {
|
||
|
#ifdef SO_PRIORITY
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else if (!strcmp(option, "broadcast")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
|
||
|
} else if (!strcmp(option, "dontroute")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_DONTROUTE, set);
|
||
|
} else if (!strcmp(option, "error")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_ERROR, set);
|
||
|
} else if (!strcmp(option, "oobinline")) {
|
||
|
return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_OOBINLINE, set);
|
||
|
} else if (!strcmp(option, "linger")) {
|
||
|
return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
|
||
|
} else if (!strcmp(option, "sndtimeo")) {
|
||
|
return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
|
||
|
} else if (!strcmp(option, "rcvtimeo")) {
|
||
|
return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
|
||
|
} else if (!strcmp(option, "bindtodevice")) {
|
||
|
#ifdef SO_BINDTODEVICE
|
||
|
return nixio__gso_b(L, sock->fd, SOL_SOCKET, SO_BINDTODEVICE, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else {
|
||
|
return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
|
||
|
" sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
|
||
|
" dontroute, bindtodevice, error, oobinline"
|
||
|
);
|
||
|
}
|
||
|
} else if (!strcmp(level, "tcp")) {
|
||
|
if (!strcmp(option, "cork")) {
|
||
|
#ifdef TCP_CORK
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else if (!strcmp(option, "nodelay")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
|
||
|
} else {
|
||
|
return luaL_argerror(L, 3, "supported values: cork, nodelay");
|
||
|
}
|
||
|
} else if (!strcmp(level, "ip")) {
|
||
|
if (!strcmp(option, "mtu")) {
|
||
|
#ifdef IP_MTU
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MTU, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else if (!strcmp(option, "hdrincl")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_HDRINCL,
|
||
|
set);
|
||
|
} else if (!strcmp(option, "multicast_loop")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||
|
set);
|
||
|
} else if (!strcmp(option, "multicast_ttl")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_TTL,
|
||
|
set);
|
||
|
} else if (!strcmp(option, "multicast_if")) {
|
||
|
return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_MULTICAST_IF,
|
||
|
set);
|
||
|
} else if (!strcmp(option, "add_membership")) {
|
||
|
return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||
|
set);
|
||
|
} else if (!strcmp(option, "drop_membership")) {
|
||
|
return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
|
||
|
set);
|
||
|
} else {
|
||
|
return luaL_argerror(L, 3,
|
||
|
"supported values: hdrincl, mtu, multicast_loop, "
|
||
|
"multicast_ttl, multicast_if, add_membership, drop_membership");
|
||
|
}
|
||
|
} else if (!strcmp(level, "ipv6")) {
|
||
|
if (!strcmp(option, "mtu")) {
|
||
|
#ifdef IPV6_MTU
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_MTU, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else if (!strcmp(option, "v6only")) {
|
||
|
#ifdef IPV6_V6ONLY
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, set);
|
||
|
#else
|
||
|
return nixio__pstatus(L, !(errno = ENOPROTOOPT));
|
||
|
#endif
|
||
|
} else if (!strcmp(option, "multicast_loop")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IPV6,
|
||
|
IPV6_MULTICAST_LOOP, set);
|
||
|
} else if (!strcmp(option, "multicast_hops")) {
|
||
|
return nixio__gso_int(L, sock->fd, IPPROTO_IPV6,
|
||
|
IPV6_MULTICAST_HOPS, set);
|
||
|
} else if (!strcmp(option, "multicast_if")) {
|
||
|
return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
|
||
|
IPV6_MULTICAST_IF, set);
|
||
|
} else if (!strcmp(option, "add_membership")) {
|
||
|
return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
|
||
|
IPV6_ADD_MEMBERSHIP, set);
|
||
|
} else if (!strcmp(option, "drop_membership")) {
|
||
|
return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
|
||
|
IPV6_DROP_MEMBERSHIP, set);
|
||
|
} else {
|
||
|
return luaL_argerror(L, 3,
|
||
|
"supported values: v6only, mtu, multicast_loop, multicast_hops,"
|
||
|
" multicast_if, add_membership, drop_membership");
|
||
|
}
|
||
|
} else {
|
||
|
return luaL_argerror(L, 2, "supported values: socket, tcp, ip, ipv6");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getsockopt()
|
||
|
*/
|
||
|
static int nixio_sock_getsockopt(lua_State *L) {
|
||
|
return nixio__getsetsockopt(L, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setsockopt()
|
||
|
*/
|
||
|
static int nixio_sock_setsockopt(lua_State *L) {
|
||
|
return nixio__getsetsockopt(L, 1);
|
||
|
}
|
||
|
|
||
|
/* module table */
|
||
|
static const luaL_reg M[] = {
|
||
|
{"setblocking", nixio_sock_setblocking},
|
||
|
{"getsockopt", nixio_sock_getsockopt},
|
||
|
{"setsockopt", nixio_sock_setsockopt},
|
||
|
{"getopt", nixio_sock_getsockopt},
|
||
|
{"setopt", nixio_sock_setsockopt},
|
||
|
{"fileno", nixio_sock_fileno},
|
||
|
{NULL, NULL}
|
||
|
};
|
||
|
|
||
|
void nixio_open_sockopt(lua_State *L) {
|
||
|
lua_pushvalue(L, -2);
|
||
|
luaL_register(L, NULL, M);
|
||
|
lua_pop(L, 1);
|
||
|
|
||
|
luaL_getmetatable(L, NIXIO_FILE_META);
|
||
|
lua_pushcfunction(L, nixio_sock_setblocking);
|
||
|
lua_setfield(L, -2, "setblocking");
|
||
|
lua_pushcfunction(L, nixio_sock_fileno);
|
||
|
lua_setfield(L, -2, "fileno");
|
||
|
lua_pop(L, 1);
|
||
|
}
|