From 2107b9a6507fe2cf5f246894c95df914a1c5b4e5 Mon Sep 17 00:00:00 2001 From: Markus Stenberg Date: Wed, 5 Dec 2012 09:54:34 +0200 Subject: [PATCH 1/4] Added if_* POSIX API wrappers under socket.iface. --- src/if.c | 112 +++++++++++++++++++++++++++++++++++++++++++++ src/if.h | 27 +++++++++++ src/luasocket.c | 2 + src/makefile | 22 +++++---- test/ifacetest.lua | 27 +++++++++++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 src/if.c create mode 100644 src/if.h create mode 100644 test/ifacetest.lua diff --git a/src/if.c b/src/if.c new file mode 100644 index 0000000..3654e93 --- /dev/null +++ b/src/if.c @@ -0,0 +1,112 @@ +/* + * $Id: if.c $ + * + * Author: Markus Stenberg + * + * Copyright (c) 2012 Markus Stenberg + * All rights reserved + * + * Created: Tue Dec 4 14:50:34 2012 mstenber + * Last modified: Wed Dec 5 09:50:59 2012 mstenber + * Edit time: 22 min + * + */ + +#include + +#include "if.h" + +#include "lauxlib.h" + +static int if_global_indextoname(lua_State *L); +static int if_global_nametoindex(lua_State *L); +static int if_global_nameindex(lua_State *L); + +static luaL_Reg func[] = { + { "indextoname", if_global_indextoname}, + { "nametoindex", if_global_nametoindex}, + { "nameindex", if_global_nameindex}, + { NULL, NULL} +}; + +int if_open(lua_State *L) +{ + lua_pushstring(L, "iface"); + lua_newtable(L); + luaL_openlib(L, NULL, func, 0); + lua_settable(L, -3); + return 0; +} + +int if_global_indextoname(lua_State *L) +{ + unsigned int ifnumber; + const char *name; + char buf[IF_NAMESIZE+1]; + + if (!lua_isnumber(L, 1)) + { + lua_pushnil(L); + lua_pushstring(L, "indextoname expects only number argument"); + return 2; + } + ifnumber = lua_tonumber(L, 1); + if (!(name = if_indextoname(ifnumber, buf))) + { + lua_pushnil(L); + lua_pushstring(L, "nonexistent interface"); + return 2; + } + lua_pushstring(L, name); + return 1; +} + +int if_global_nametoindex(lua_State *L) +{ + unsigned int ifnumber; + if (!lua_isstring(L, 1)) + { + lua_pushnil(L); + lua_pushstring(L, "nametoindex expects only string argument"); + return 2; + } + if (!(ifnumber = if_nametoindex(lua_tostring(L, 1)))) + { + lua_pushnil(L); + lua_pushstring(L, "nonexistent interface"); + return 2; + } + lua_pushnumber(L, ifnumber); + return 1; +} + +int if_global_nameindex(lua_State *L) +{ + struct if_nameindex *ni, *oni; + int i = 1; + oni = ni = if_nameindex(); + lua_newtable(L); + while (ni && ni->if_index && *(ni->if_name)) + { + /* at result[i], we store.. */ + lua_pushnumber(L, i); + + /* new table with two items - index, name*/ + lua_newtable(L); + lua_pushstring(L, "index"); + lua_pushnumber(L, ni->if_index); + lua_settable(L, -3); + + lua_pushstring(L, "name"); + lua_pushstring(L, ni->if_name); + lua_settable(L, -3); + + /* Then, actually store it */ + lua_settable(L, -3); + + i++; + ni++; + } + if_freenameindex(oni); + return 1; +} diff --git a/src/if.h b/src/if.h new file mode 100644 index 0000000..dc7faf8 --- /dev/null +++ b/src/if.h @@ -0,0 +1,27 @@ +/* + * $Id: if.h $ + * + * Author: Markus Stenberg + * + * Copyright (c) 2012 cisco Systems, Inc. + * + * Created: Tue Dec 4 14:37:24 2012 mstenber + * Last modified: Tue Dec 4 14:51:43 2012 mstenber + * Edit time: 7 min + * + */ + +/* This module provides Lua wrapping for the advanced socket API + * defined in RFC3542, or mainly, the access to the system's interface + * list. It is necessary for use of recvmsg/sendmsg. + * + * TODO - Do something clever with Windows? + */ +#ifndef IF_H +#define IF_H + +#include "lua.h" + +int if_open(lua_State *L); + +#endif /* IF_H */ diff --git a/src/luasocket.c b/src/luasocket.c index b43114e..88b7e71 100644 --- a/src/luasocket.c +++ b/src/luasocket.c @@ -34,6 +34,7 @@ #include "tcp.h" #include "udp.h" #include "select.h" +#include "if.h" /*-------------------------------------------------------------------------*\ * Internal function prototypes @@ -54,6 +55,7 @@ static const luaL_Reg mod[] = { {"tcp", tcp_open}, {"udp", udp_open}, {"select", select_open}, + {"iface", if_open}, {NULL, NULL} }; diff --git a/src/makefile b/src/makefile index 7e5f2e2..3b1eada 100644 --- a/src/makefile +++ b/src/makefile @@ -26,7 +26,7 @@ LUAV?=5.1 DEBUG?=NODEBUG # where lua headers are found for macosx builds -# LUAINC_macosx: +# LUAINC_macosx: # /opt/local/include LUAINC_macosx_base?=/opt/local/include LUAINC_macosx?=$(LUAINC_macosx_base)/lua$(LUAV) @@ -34,9 +34,9 @@ LUAINC_macosx?=$(LUAINC_macosx_base)/lua$(LUAV) # What happens when more than one Lua version is installed? LUAPREFIX_macosx?=/opt/local/ -# LUAINC_linux: -# /usr/include/lua$(LUAV) -# /usr/local/include +# LUAINC_linux: +# /usr/include/lua$(LUAV) +# /usr/local/include # /usr/local/include/lua$(LUAV) # where lua headers are found for linux builds LUAINC_linux_base?=/usr/include @@ -104,7 +104,7 @@ DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN -DLUA_COMPAT_MODULE \ -DMIME_API='__attribute__((visibility("default")))' CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \ -fvisibility=hidden -LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc SOCKET_macosx=usocket.o @@ -119,7 +119,7 @@ DEF_linux=-DLUASOCKET_$(DEBUG) \ -DMIME_API='__attribute__((visibility("default")))' CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -Wshadow -Wextra -Wimplicit -O2 -ggdb3 -fpic \ -fvisibility=hidden -LDFLAGS_linux=-O -shared -fpic -o +LDFLAGS_linux=-O -shared -fpic -o LD_linux=gcc SOCKET_linux=usocket.o @@ -183,6 +183,7 @@ SOCKET_OBJS= \ auxiliar.$(O) \ options.$(O) \ inet.$(O) \ + if.$(O) \ $(SOCKET) \ except.$(O) \ select.$(O) \ @@ -258,20 +259,20 @@ none: all: $(SOCKET_SO) $(MIME_SO) $(SOCKET_SO): $(SOCKET_OBJS) - $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ + $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ $(MIME_SO): $(MIME_OBJS) - $(LD) $(MIME_OBJS) $(LDFLAGS)$@ + $(LD) $(MIME_OBJS) $(LDFLAGS)$@ all-unix: all $(UNIX_SO) $(SERIAL_SO) $(UNIX_SO): $(UNIX_OBJS) - $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ + $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ $(SERIAL_SO): $(SERIAL_OBJS) $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ -install: +install: $(INSTALL_DIR) $(INSTALL_TOP_SHARE) $(INSTALL_DATA) $(TO_TOP_SHARE) $(INSTALL_TOP_SHARE) $(INSTALL_DIR) $(INSTALL_SOCKET_SHARE) @@ -301,6 +302,7 @@ auxiliar.$(O): auxiliar.c auxiliar.h buffer.$(O): buffer.c buffer.h io.h timeout.h except.$(O): except.c except.h inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +if.$(O): if.c if.h io.$(O): io.c io.h timeout.h luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ diff --git a/test/ifacetest.lua b/test/ifacetest.lua new file mode 100644 index 0000000..158f114 --- /dev/null +++ b/test/ifacetest.lua @@ -0,0 +1,27 @@ +#!/usr/bin/env lua +-- -*-lua-*- +-- +-- $Id: ifacetest.lua $ +-- +-- Author: Markus Stenberg +-- +-- Copyright (c) 2012 cisco Systems, Inc. +-- +-- Created: Wed Dec 5 09:42:12 2012 mstenber +-- Last modified: Wed Dec 5 09:51:17 2012 mstenber +-- Edit time: 3 min +-- + +local socket = require 'socket' +local iface = socket.iface + +local a = iface.nameindex() +assert(#a > 0) +local o = a[1] +assert(o.index) +assert(o.name) +assert(iface.nametoindex(o.name)) +assert(not iface.nametoindex('gargle')) +assert(iface.indextoname(o.index)) +assert(not iface.indextoname(-123)) + From a81ac2150d0790372b74f1cecd7b967ddb2f11ea Mon Sep 17 00:00:00 2001 From: Markus Stenberg Date: Wed, 5 Dec 2012 18:41:06 +0200 Subject: [PATCH 2/4] Converted code to use getaddrinfo/getnameinfo (available ~everywhere like *BSD, Linux, Windows at least), and added various ipv6 socket options' get+set. --- src/inet.c | 127 ++++++++++++++--------------------- src/options.c | 95 +++++++++++++++++++++++++- src/options.h | 9 +++ src/udp.c | 148 ++++++++++++++++------------------------- test/ipv6mcasttest.lua | 116 ++++++++++++++++++++++++++++++++ 5 files changed, 324 insertions(+), 171 deletions(-) create mode 100644 test/ipv6mcasttest.lua diff --git a/src/inet.c b/src/inet.c index dfee700..940c0e0 100644 --- a/src/inet.c +++ b/src/inet.c @@ -231,45 +231,31 @@ static int inet_global_gethostname(lua_State *L) \*-------------------------------------------------------------------------*/ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) { - switch (family) { - case PF_INET: { - struct sockaddr_in peer; - socklen_t peer_len = sizeof(peer); - char name[INET_ADDRSTRLEN]; - if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(errno)); - return 2; - } else { - inet_ntop(family, &peer.sin_addr, name, sizeof(name)); - lua_pushstring(L, name); - lua_pushnumber(L, ntohs(peer.sin_port)); - lua_pushliteral(L, "inet"); - return 3; - } - } - case PF_INET6: { - struct sockaddr_in6 peer; - socklen_t peer_len = sizeof(peer); - char name[INET6_ADDRSTRLEN]; - if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(errno)); - return 2; - } else { - inet_ntop(family, &peer.sin6_addr, name, sizeof(name)); - lua_pushstring(L, name); - lua_pushnumber(L, ntohs(peer.sin6_port)); - lua_pushliteral(L, "inet6"); - return 3; - } - return 2; - } - default: - lua_pushnil(L); - lua_pushfstring(L, "unknown family %d", family); - return 2; - } + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + if ((err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, + port, 6, + NI_NUMERICHOST | NI_NUMERICSERV))) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushstring(L, port); + if (family == PF_INET) + lua_pushliteral(L, "inet"); + else + lua_pushliteral(L, "inet6"); + return 3; } /*-------------------------------------------------------------------------*\ @@ -277,44 +263,31 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) \*-------------------------------------------------------------------------*/ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) { - switch (family) { - case PF_INET: { - struct sockaddr_in local; - socklen_t local_len = sizeof(local); - char name[INET_ADDRSTRLEN]; - if (getsockname(*ps, (SA *) &local, &local_len) < 0) { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(errno)); - return 2; - } else { - inet_ntop(family, &local.sin_addr, name, sizeof(name)); - lua_pushstring(L, name); - lua_pushnumber(L, ntohs(local.sin_port)); - lua_pushliteral(L, "inet"); - return 3; - } - } - case PF_INET6: { - struct sockaddr_in6 local; - socklen_t local_len = sizeof(local); - char name[INET6_ADDRSTRLEN]; - if (getsockname(*ps, (SA *) &local, &local_len) < 0) { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(errno)); - return 2; - } else { - inet_ntop(family, &local.sin6_addr, name, sizeof(name)); - lua_pushstring(L, name); - lua_pushnumber(L, ntohs(local.sin6_port)); - lua_pushliteral(L, "inet6"); - return 3; - } - } - default: - lua_pushnil(L); - lua_pushfstring(L, "unknown family %d", family); - return 2; - } + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + if ((err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, + port, 6, + NI_NUMERICHOST | NI_NUMERICSERV))) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushstring(L, port); + if (family == PF_INET) + lua_pushliteral(L, "inet"); + else + lua_pushliteral(L, "inet6"); + return 3; } /*=========================================================================*\ diff --git a/src/options.c b/src/options.c index 6cae7ee..d3c347a 100644 --- a/src/options.c +++ b/src/options.c @@ -14,8 +14,11 @@ * Internal functions prototypes \*=========================================================================*/ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_setint(lua_State *L, p_socket ps, int level, int name); +static int opt_getint(lua_State *L, p_socket ps, int level, int name); static int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len); static int opt_get(lua_State *L, p_socket ps, int level, int name, @@ -106,6 +109,26 @@ int opt_set_broadcast(lua_State *L, p_socket ps) return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); } +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); @@ -116,6 +139,16 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); } +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + int opt_set_linger(lua_State *L, p_socket ps) { struct linger li; /* obj, name, table */ @@ -150,9 +183,7 @@ int opt_get_linger(lua_State *L, p_socket ps) int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) { - int val = (int) luaL_checknumber(L, 3); /* obj, name, int */ - return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_TTL, - (char *) &val, sizeof(val)); + return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); } int opt_set_ip_multicast_if(lua_State *L, p_socket ps) @@ -179,6 +210,8 @@ int opt_get_ip_multicast_if(lua_State *L, p_socket ps) return 1; } + + int opt_set_ip_add_membership(lua_State *L, p_socket ps) { return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); @@ -189,6 +222,21 @@ int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); } +int opt_set_ip6_add_membership(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +} + +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP); +} + +int opt_get_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} + int opt_set_ip6_v6only(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); @@ -218,6 +266,31 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); } +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ipv6_mreq val; /* obj, opt-name, table */ + memset(&val, 0, sizeof(val)); + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + /* By default we listen to all interfaces. However, interface= can + override it. */ + if (!lua_isnil(L, -1)) + { + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + /* XXX - support non-numeric interface specification? */ + } + val.ipv6mr_interface = lua_tonumber(L, -1); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + static int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) { @@ -260,3 +333,19 @@ static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); } +static int opt_getint(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushnumber(L, val); + return 1; +} + +static int opt_setint(lua_State *L, p_socket ps, int level, int name) +{ + int val = (int) lua_tonumber(L, 3); /* obj, name, int */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} diff --git a/src/options.h b/src/options.h index 55447f7..92bd125 100644 --- a/src/options.h +++ b/src/options.h @@ -32,6 +32,11 @@ int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps); int opt_set_ip_multicast_loop(lua_State *L, p_socket ps); int opt_set_ip_add_membership(lua_State *L, p_socket ps); int opt_set_ip_drop_membersip(lua_State *L, p_socket ps); +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_set_ip6_add_membership(lua_State *L, p_socket ps); +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps); int opt_set_ip6_v6only(lua_State *L, p_socket ps); /* supported options for getoption */ @@ -42,6 +47,10 @@ int opt_get_linger(lua_State *L, p_socket ps); int opt_get_reuseaddr(lua_State *L, p_socket ps); int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); int opt_get_ip_multicast_if(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_v6only(lua_State *L, p_socket ps); /* invokes the appropriate option handler */ int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); diff --git a/src/udp.c b/src/udp.c index 4cd9a41..075500e 100644 --- a/src/udp.c +++ b/src/udp.c @@ -77,6 +77,11 @@ static t_opt optset[] = { {"ip-multicast-loop", opt_set_ip_multicast_loop}, {"ip-add-membership", opt_set_ip_add_membership}, {"ip-drop-membership", opt_set_ip_drop_membersip}, + {"ipv6-unicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-hops",opt_set_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_set_ip6_multicast_loop}, + {"ipv6-add-membership", opt_set_ip6_add_membership}, + {"ipv6-drop-membership", opt_set_ip6_drop_membersip}, {"ipv6-v6only", opt_set_ip6_v6only}, {NULL, NULL} }; @@ -85,6 +90,10 @@ static t_opt optset[] = { static t_opt optget[] = { {"ip-multicast-if", opt_get_ip_multicast_if}, {"ip-multicast-loop", opt_get_ip_multicast_loop}, + {"ipv6-unicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-hops",opt_get_ip6_unicast_hops}, + {"ipv6-multicast-loop",opt_get_ip6_multicast_loop}, + {"ipv6-v6only", opt_get_ip6_v6only}, {NULL, NULL} }; @@ -151,39 +160,25 @@ static int meth_sendto(lua_State *L) { size_t count, sent = 0; const char *data = luaL_checklstring(L, 2, &count); const char *ip = luaL_checkstring(L, 3); - unsigned short port = (unsigned short) luaL_checknumber(L, 4); + /* unsigned short port = (unsigned short) luaL_checknumber(L, 4); */ p_timeout tm = &udp->tm; int err; - switch (udp->family) { - case PF_INET: { - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - if (!inet_pton(AF_INET, ip, &addr.sin_addr)) - luaL_argerror(L, 3, "invalid ip address"); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm); - break; - } - case PF_INET6: { - struct sockaddr_in6 addr; - memset(&addr, 0, sizeof(addr)); - if (!inet_pton(AF_INET6, ip, &addr.sin6_addr)) - luaL_argerror(L, 3, "invalid ip address"); - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port); - timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm); - break; - } - default: - lua_pushnil(L); - lua_pushfstring(L, "unknown family %d", udp->family); - return 2; - } + struct addrinfo aihint; + struct addrinfo *ai; + + memset(&aihint, 0, sizeof(aihint)); + aihint.ai_family = udp->family; + aihint.ai_socktype = SOCK_DGRAM; + if ((err = getaddrinfo(ip, lua_tostring(L, 4), &aihint, &ai))) + { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, + ai->ai_addr, ai->ai_addrlen, tm); + freeaddrinfo(ai); if (err != IO_DONE) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); @@ -220,69 +215,40 @@ static int meth_receive(lua_State *L) { /*-------------------------------------------------------------------------*\ * Receives data and sender from a UDP socket \*-------------------------------------------------------------------------*/ -static int meth_receivefrom(lua_State *L) { - p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); - char buffer[UDP_DATAGRAMSIZE]; - size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); - int err; - p_timeout tm = &udp->tm; - timeout_markstart(tm); - count = MIN(count, sizeof(buffer)); - switch (udp->family) { - case PF_INET: { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - err = socket_recvfrom(&udp->sock, buffer, count, &got, - (SA *) &addr, &addr_len, tm); - /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err == IO_DONE) { - char addrstr[INET_ADDRSTRLEN]; - lua_pushlstring(L, buffer, got); - if (!inet_ntop(AF_INET, &addr.sin_addr, - addrstr, sizeof(addrstr))) { - lua_pushnil(L); - lua_pushstring(L, "invalid source address"); - return 2; - } - lua_pushstring(L, addrstr); - lua_pushnumber(L, ntohs(addr.sin_port)); - return 3; - } - break; - } - case PF_INET6: { - struct sockaddr_in6 addr; - socklen_t addr_len = sizeof(addr); - err = socket_recvfrom(&udp->sock, buffer, count, &got, - (SA *) &addr, &addr_len, tm); - /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err == IO_DONE) { - char addrstr[INET6_ADDRSTRLEN]; - lua_pushlstring(L, buffer, got); - if (!inet_ntop(AF_INET6, &addr.sin6_addr, - addrstr, sizeof(addrstr))) { - lua_pushnil(L); - lua_pushstring(L, "invalid source address"); - return 2; - } - lua_pushstring(L, addrstr); - lua_pushnumber(L, ntohs(addr.sin6_port)); - return 3; - } - break; - } - default: - lua_pushnil(L); - lua_pushfstring(L, "unknown family %d", udp->family); - return 2; - } +static int meth_receivefrom(lua_State *L) +{ + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + char buffer[UDP_DATAGRAMSIZE]; + size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); + int err; + p_timeout tm = &udp->tm; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + timeout_markstart(tm); + count = MIN(count, sizeof(buffer)); + err = socket_recvfrom(&udp->sock, buffer, count, &got, + (SA *) &addr, &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err == IO_CLOSED) + err = IO_DONE; + if (err != IO_DONE) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); return 2; + } + if ((err = getnameinfo((struct sockaddr *)&addr, addr_len, + addrstr, INET6_ADDRSTRLEN, + portstr, 6, + NI_NUMERICHOST | NI_NUMERICSERV))) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + lua_pushstring(L, addrstr); + lua_pushstring(L, portstr); + return 3; } /*-------------------------------------------------------------------------*\ diff --git a/test/ipv6mcasttest.lua b/test/ipv6mcasttest.lua new file mode 100644 index 0000000..8084443 --- /dev/null +++ b/test/ipv6mcasttest.lua @@ -0,0 +1,116 @@ +#!/usr/bin/env lua +-- -*-lua-*- +-- +-- $Id: ipv6mcasttest.lua $ +-- +-- Author: Markus Stenberg +-- +-- Copyright (c) 2012 cisco Systems, Inc. +-- +-- Created: Wed Dec 5 09:55:46 2012 mstenber +-- Last modified: Wed Dec 5 18:33:09 2012 mstenber +-- Edit time: 50 min +-- + +-- exercise the new IPv6 related functionality: + +-- join/leave IPv6 multicast group, send/receive IPv6 message using +-- sendmsg/recvmsg API + +require 'socket' + +local mcast = 'ff02::fb' + +local port = 4242 +local ifname = 'eth2' +--local ifname = 'lo' -- should not work - no multicast on lo in Linux? +local testdata = 'foobar' + +function setup_socket(port) + local s = socket.udp6() + s:setoption('ipv6-v6only', true) + if port + then + s:setoption('reuseaddr', true) + --local r, err = s:setsockname('::1', port) + local r, err = s:setsockname('*', port) + assert(r, 'setsockname failed ' .. tostring(err)) + --print("getsockname", s:getsockname()) + end + assert(tostring(s):find("udp{unconnected}")) + return s +end + +function setup_connected(host, port) + local c = setup_socket() + assert(host and port) + local r, err = c:setpeername(host, port) + assert(r, 'setpeername failed ' .. tostring(err)) + --print('got from setpeername', r, err) + --print("getsockname", c:getsockname()) + assert(tostring(c):find("udp{connected}"), 'not connected', tostring(c)) + --print("getpeername", c:getpeername()) + return c +end + +function my_setoption(s, k, v) + local old = s:getoption(k) + print('before set', k, old) + local r, err = s:setoption(k, v) + assert(r, 'setoption failed ' .. tostring(err)) + local new = s:getoption(k) + print('after set', k, new) + assert(v == new) +end + +-- specific interface +--local ifindex = socket.iface.nametoindex(ifname) + +-- all interfaces +local ifindex = nil + +local s = setup_socket(port) + +local mct = {multiaddr=mcast, interface=ifindex} +local r, err = s:setoption('ipv6-drop-membership', mct) +-- don't check result - it may or may not fail +local r, err = s:setoption('ipv6-add-membership', mct) +assert(r, 'ipv6-add-membership failed', err) + +--local c = setup_connected(mcast .. '%lo', port) +--c:send('foo') +local c = setup_socket() + +-- this seems to be the default anyway? +my_setoption(c, 'ipv6-unicast-hops', 255) +my_setoption(c, 'ipv6-multicast-loop', true) +my_setoption(c, 'ipv6-multicast-hops', 255) +my_setoption(s, 'ipv6-multicast-hops', 255) + +local r, err = c:sendto(testdata, mcast .. '%' .. ifname, port) +assert(r, 'sendto failed') + +-- make sure sending both to mcast address, and link-local +-- address on the chosen interface works +local r, err = c:sendto(testdata, mcast .. '%' .. ifname, port) +assert(r, 'sendto failed (mcast)') + +local data, host, port = s:receivefrom() +assert(data) +print('recvfrom', host, port) + +-- make sure we can send stuff back to link-local addr as well' +local r, err = s:sendto(testdata, host, port) +assert(r, 'sendto failed (ll)') + +-- and it is received too +local data, host, port = c:receivefrom() +assert(data) +print('recvfrom', host, port) + +-- now, finally, we can get rid of mcast group +local r, err = s:setoption('ipv6-drop-membership', mct) +assert(r, 'ipv6-drop-membership failed', err) +--local c = setup_connected(port) + + From c9dab5bbd227956ad2294f1ba20ebfb9a90c48bd Mon Sep 17 00:00:00 2001 From: Markus Stenberg Date: Wed, 5 Dec 2012 18:49:16 +0200 Subject: [PATCH 3/4] Fixed compilation to work on OS X. --- src/if.c | 6 ++++-- src/makefile | 3 ++- src/options.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/if.c b/src/if.c index 3654e93..adda71d 100644 --- a/src/if.c +++ b/src/if.c @@ -7,11 +7,13 @@ * All rights reserved * * Created: Tue Dec 4 14:50:34 2012 mstenber - * Last modified: Wed Dec 5 09:50:59 2012 mstenber - * Edit time: 22 min + * Last modified: Wed Dec 5 18:48:55 2012 mstenber + * Edit time: 23 min * */ +#include +#include #include #include "if.h" diff --git a/src/makefile b/src/makefile index 3b1eada..bdcc07a 100644 --- a/src/makefile +++ b/src/makefile @@ -29,7 +29,8 @@ DEBUG?=NODEBUG # LUAINC_macosx: # /opt/local/include LUAINC_macosx_base?=/opt/local/include -LUAINC_macosx?=$(LUAINC_macosx_base)/lua$(LUAV) +#LUAINC_macosx?=$(LUAINC_macosx_base)/lua$(LUAV) +LUAINC_macosx?=$(LUAINC_macosx_base) # FIXME default should this default to fink or to macports? # What happens when more than one Lua version is installed? LUAPREFIX_macosx?=/opt/local/ diff --git a/src/options.c b/src/options.c index d3c347a..11411a6 100644 --- a/src/options.c +++ b/src/options.c @@ -10,6 +10,22 @@ #include "options.h" #include "inet.h" +/* Some platforms use IPV6_JOIN_GROUP instead if + * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ + +/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ + + /*=========================================================================*\ * Internal functions prototypes \*=========================================================================*/ From 9ed54c2eaf74f8c8a9fc121152aa7bb7a84401c7 Mon Sep 17 00:00:00 2001 From: Markus Stenberg Date: Wed, 5 Dec 2012 18:51:34 +0200 Subject: [PATCH 4/4] Oh, forgot one personal copyright. Replaced with Cisco. --- src/if.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/if.c b/src/if.c index adda71d..db231aa 100644 --- a/src/if.c +++ b/src/if.c @@ -3,12 +3,11 @@ * * Author: Markus Stenberg * - * Copyright (c) 2012 Markus Stenberg - * All rights reserved + * Copyright (c) 2012 cisco Systems, Inc. * * Created: Tue Dec 4 14:50:34 2012 mstenber - * Last modified: Wed Dec 5 18:48:55 2012 mstenber - * Edit time: 23 min + * Last modified: Wed Dec 5 18:51:08 2012 mstenber + * Edit time: 24 min * */