summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Roberts <vieuxtech@gmail.com>2009-03-31 15:43:42 -0700
committerSam Roberts <vieuxtech@gmail.com>2009-03-31 15:43:42 -0700
commit752a3533f46a4dc680c86575e78efc11b5cd470f (patch)
tree5fd66f12f1a39bc145b7710638b36ff2357fbae8
parent7b46533123ccaac7fd37362502ec8050a9d9b972 (diff)
downloadlibnet-752a3533f46a4dc680c86575e78efc11b5cd470f.tar.gz
Unmerged lua bindings for parts of libnet's API.
-rwxr-xr-xunmerged/net-test68
-rw-r--r--unmerged/net.c562
2 files changed, 630 insertions, 0 deletions
diff --git a/unmerged/net-test b/unmerged/net-test
new file mode 100755
index 0000000..87c4604
--- /dev/null
+++ b/unmerged/net-test
@@ -0,0 +1,68 @@
+#!/usr/bin/env lua5.1
+
+package.path = package.path .. ";".."wurldtech/lgram/?.lua"
+package.cpath = package.cpath .. ";".."wurldtech/lgram/?.so"
+
+require"grammar"
+require"net"
+
+local function check_ipv4()
+ local n = net.init("link", "eth0")
+ n:ipv4{src="1.2.3.1", dst="1.2.3.2", protocol=2, len=20+4, options="AAAA"}
+ n:eth{src="01:02:03:04:05:01", dst="01:02:03:04:05:02"}
+
+ ok=pcall(n.ipv4, n, {src="1.2.3.1", dst="1.2.3.2", protocol=2, len=20+4, options="AAAA"})
+ assert(not ok, "net:ipv4 fails to detect incorrect usage of IPv4 options")
+end
+
+local function check_ptag()
+ local n = net.init("link", "eth0")
+ eth = n:eth{src="01:02:03:04:05:01", dst="01:02:03:04:05:02"}
+ ok=pcall(n.ipv4, n, {src="1.2.3.1", dst="1.2.3.2", protocol=2, len=20+4, options="AAAA", ptag = eth})
+ assert(not ok, "net:ipv4 fails to detect specifying a ptag when creating IPv4 options")
+end
+
+local function check_first()
+ local n = net.init("link", "eth0")
+ ok=pcall(n.ipv4, n, {src="1.2.3.1", dst="1.2.3.2", protocol=2, len=20+4, options="AAAA"})
+ eth = n:eth{src="01:02:03:04:05:01", dst="01:02:03:04:05:02"}
+ assert(ok, "net:ipv4 fails to construct ipv4 options correctly")
+end
+
+local q = grammar.quote
+
+n = net.init("link", "eth0")
+
+print(q(net.pton("01:02:03:04:05:06")))
+print(q(net.pton("1.2.3.4")))
+print(q(net.pton("::1")))
+
+n:udp{src=3, dst=5, payload="XXX"}
+ipv4 = n:ipv4{src="1.2.3.1", dst="1.2.3.2", protocol=17, len=20+8+3}
+n:eth{src="01:02:03:04:05:01", dst="01:02:03:04:05:02"}
+
+print(n:dump())
+
+n:write()
+
+b1 = n:block()
+print(#b1, q(b1))
+
+n:ipv4{src="1.2.3.1", dst="1.2.3.2", protocol=17, len=20+8+3, ptag=ipv4}
+
+--print(q({src="1.2.3.1", dst="1.2.3.2", protocol=17, len=20+8+3, ptag=ipv4}))
+
+print(n:dump())
+
+b2 = n:block()
+
+print("b1=", q(b1))
+print("b2=", q(b2))
+
+assert(b1 == b2)
+
+check_ipv4()
+check_ptag()
+check_first()
+
+
diff --git a/unmerged/net.c b/unmerged/net.c
new file mode 100644
index 0000000..60ebe67
--- /dev/null
+++ b/unmerged/net.c
@@ -0,0 +1,562 @@
+/*
+Copyright (c) 2009 Wurldtech Security Technologies All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <dnet.h>
+#include <libnet.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+
+#undef NET_DUMP
+//#define NET_DUMP
+
+/*
+Get field from arg table, errors if argt is not a table, returns
+0 if field not found, otherwise pushes field value and returns
+index.
+*/
+static int v_arg(lua_State* L, int argt, const char* field)
+{
+ luaL_checktype(L, argt, LUA_TTABLE);
+
+ lua_getfield(L, argt, field);
+
+ if(lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ return 0;
+ }
+ return lua_gettop(L);
+}
+
+static const char* v_arg_lstring(lua_State* L, int argt, const char* field, size_t* size, const char* def)
+{
+ if(!v_arg(L, argt, field))
+ {
+ if(def) {
+ lua_pushstring(L, def);
+ return lua_tolstring(L, -1, size);
+ } else {
+ const char* msg = lua_pushfstring(L, "%s is missing", field);
+ luaL_argerror(L, argt, msg);
+ }
+ }
+
+ if(!lua_tostring(L, -1)) {
+ const char* msg = lua_pushfstring(L, "%s is not a string", field);
+ luaL_argerror(L, argt, msg);
+ }
+
+ return lua_tolstring(L, -1, size);
+}
+
+static const char* v_arg_string(lua_State* L, int argt, const char* field)
+{
+ return v_arg_lstring(L, argt, field, NULL, NULL);
+}
+
+static int v_arg_integer_get_(lua_State* L, int argt, const char* field)
+{
+ if(lua_type(L, -1) != LUA_TNUMBER) {
+ const char* msg = lua_pushfstring(L, "%s is not an integer", field);
+ luaL_argerror(L, argt, msg);
+ }
+
+ return lua_tointeger(L, -1);
+}
+
+static int v_arg_integer(lua_State* L, int argt, const char* field)
+{
+ if(!v_arg(L, argt, field))
+ {
+ const char* msg = lua_pushfstring(L, "%s is missing", field);
+ luaL_argerror(L, argt, msg);
+ }
+
+ return v_arg_integer_get_(L, argt, field);
+}
+
+static int v_arg_integer_opt(lua_State* L, int argt, const char* field, int def)
+{
+ if(!v_arg(L, argt, field))
+ {
+ lua_pushinteger(L, def);
+ return lua_tointeger(L, -1);
+ }
+
+ return v_arg_integer_get_(L, argt, field);
+}
+
+static void v_obj_metatable(lua_State* L, const char* regid, const struct luaL_reg methods[])
+{
+ // metatable = { ... methods ... }
+ luaL_newmetatable(L, regid);
+ luaL_register(L, NULL, methods);
+ // metatable["__index"] = metatable
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+}
+
+
+
+
+#define L_NET_REGID "wt.net"
+
+
+static int net_error(lua_State* L, libnet_t* l)
+{
+ return luaL_error(L, "%s", libnet_geterror(l));
+}
+
+static int check_error(lua_State* L, libnet_t* l, int r)
+{
+ if(r < 0) {
+ return net_error(L, l);
+ }
+ return r;
+}
+
+static uint32_t check_ip_pton(lua_State* L, const char* p, const char* name)
+{
+ uint32_t n = 0;
+ if(ip_pton(p, &n) < 0) {
+ return luaL_error(L, "ip_pton failed on %s '%s'", name, p);
+ }
+ return n;
+}
+
+static eth_addr_t check_eth_pton(lua_State* L, const char* p, const char* name)
+{
+ eth_addr_t n;
+ if(eth_pton(p, &n) < 0) {
+ luaL_error(L, "eth_pton failed on %s '%s'", name, p);
+ }
+ return n;
+}
+
+static int lnet_arg_ptag(lua_State* L, int targ)
+{
+ return v_arg_integer_opt(L, targ, "ptag", LIBNET_PTAG_INITIALIZER);
+}
+
+/*-
+- net:destroy()
+
+Manually destroy a net object, freeing it's resources (this will happen
+on garbage collection if not done explicitly).
+*/
+static int lnet_destroy (lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+
+ if(*ud)
+ libnet_destroy(*ud);
+
+ *ud = NULL;
+
+ return 0;
+}
+
+/*-
+- net:dump()
+
+Write summary of protocol blocks to stdout.
+*/
+static const char* type_string(u_int8_t type)
+{
+ switch(type) {
+ case LIBNET_PBLOCK_ETH_H: return "eth";
+ case LIBNET_PBLOCK_IPV4_H: return "ip4";
+ case LIBNET_PBLOCK_IPO_H: return "ipo";
+ case LIBNET_PBLOCK_IPDATA: return "ipd";
+ }
+ return "?";
+}
+static int lnet_dump(lua_State* L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+ libnet_pblock_t* p = (*ud)->protocol_blocks;
+ int strings = 0;
+
+ while(p) {
+ /* h_len is header length for checksumming? "chksum length"? */
+ char str[1024];
+ sprintf(str, "tag %d flags %d type %s/%#x buf %p b_len %2u h_len %2u ip_offset %2u, copied %u\n",
+ p->ptag, p->flags, type_string(p->type), p->type,
+ p->buf, p->b_len, p->h_len, p->ip_offset, p->copied);
+ lua_pushstring(L, str);
+ p = p->next;
+ strings++;
+ }
+ lua_pushfstring(L, "link_offset %d aligner %d total_size %u nblocks %d\n",
+ (*ud)->link_offset, (*ud)->aligner, (*ud)->total_size, (*ud)->n_pblocks);
+ strings++;
+
+ lua_concat(L, strings);
+
+ return 1;
+}
+
+/*-
+- str = net:block()
+
+Coalesce the protocol blocks into a single chunk, and return.
+*/
+static int lnet_block(lua_State* L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+ u_int32_t len;
+ u_int8_t *packet = NULL;
+
+ int r = libnet_pblock_coalesce(*ud, &packet, &len);
+
+ check_error(L, *ud, r);
+
+ lua_pushlstring(L, (char*) packet, len);
+
+ return 1;
+}
+
+/*-
+- net:write()
+
+Write the packet (which must previously have been built up inside the context).
+*/
+static int lnet_write(lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+#ifdef NET_DUMP
+ lnet_dump(L);
+#endif
+
+ int r = libnet_write(*ud);
+
+ check_error(L, *ud, r);
+
+ lua_pushinteger(L, r);
+
+ return 1;
+}
+
+/*-
+- ptag = net:udp{src=NUM, dst=NUM, len=NUM, payload=STR, ptag=int}
+
+Build UDP packet inside net context.
+
+ptag is optional, defaults to creating a new protocol block
+*/
+static int lnet_udp (lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+ int src = v_arg_integer(L, 2, "src");
+ int dst = v_arg_integer(L, 2, "dst");
+
+ size_t payloadsz = 0;
+ const char* payload = v_arg_lstring(L, 2, "payload", &payloadsz, "");
+ int len = v_arg_integer_opt(L, 2, "len", LIBNET_UDP_H + payloadsz);
+ int cksum = 0;
+ int ptag = lnet_arg_ptag(L, 2);
+
+ if(payloadsz == 0) {
+ payload = NULL;
+ }
+
+ ptag = libnet_build_udp(src, dst, len, cksum, (uint8_t*)payload, payloadsz, *ud, ptag);
+ check_error(L, *ud, ptag);
+ lua_pushinteger(L, ptag);
+ return 1;
+}
+
+/*-
+- ptag = n:ipv4{len=int, protocol=int, src=ipaddr, dst=ipaddr, payload=str, ptag=int, options=ip_options}
+
+ptag is optional, defaults to creating a new protocol block
+options is optional
+*/
+static int lnet_ipv4 (lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+ int len = v_arg_integer(L, 2, "len"); // FIXME - should be optional!
+ int tos = 0;
+ int id = 0;
+ int offset = 0;
+ int ttl = 64;
+ int protocol = v_arg_integer(L, 2, "protocol");
+ int cksum = 0; // 0 is a flag requesting libnet to fill in correct cksum
+ const char* src = v_arg_string(L, 2, "src");
+ const char* dst = v_arg_string(L, 2, "dst");
+ size_t payloadsz = 0;
+ const char* payload = v_arg_lstring(L, 2, "payload", &payloadsz, "");
+ int ip_options_tag = 0;
+ int ptag = lnet_arg_ptag(L, 2);
+ size_t optionsz = 0;
+ const char* options = v_arg_lstring(L, 2, "options", &optionsz, "");
+
+ if(payloadsz == 0) {
+ payload = NULL;
+ }
+
+#ifdef NET_DUMP
+ printf("net ipv4 src %s dst %s len %d payloadsz %lu ptag %d optionsz %lu\n", src, dst, len, payloadsz, ptag, optionsz);
+#endif
+ uint32_t src_n = check_ip_pton(L, src, "src");
+ uint32_t dst_n = check_ip_pton(L, dst, "dst");
+
+ if (optionsz) {
+ /* Need to do multiple sanity checks before we add IP options */
+ /* 1. Confirm we're the first protocol block*/
+ if ((*ud)->pblock_end != NULL) {
+ /* 2. Cannot specify a ptag */
+ if (ptag)
+ return luaL_error(L,
+ "ptag cannot be specified when using IPv4 options");
+ /* 3. Ensure that we do not add the IP Options after the wrong block */
+ else if ((*ud)->pblock_end->type == LIBNET_PBLOCK_ETH_H
+ || (*ud)->pblock_end->type == LIBNET_PBLOCK_IPV4_H
+ || (*ud)->pblock_end->type == LIBNET_PBLOCK_IPO_H)
+ return luaL_error(L, "Unsupported usage of IPv4 options");
+ }
+ ip_options_tag = libnet_build_ipv4_options((uint8_t*) options,
+ optionsz, *ud, 0);
+ check_error(L, *ud, ip_options_tag);
+ }
+
+ ptag = libnet_build_ipv4(len, tos, id, offset, ttl, protocol, cksum, src_n,
+ dst_n, (uint8_t*) payload, payloadsz, *ud, ptag);
+ check_error(L, *ud, ptag);
+ lua_pushinteger(L, ptag);
+ return 1;
+}
+
+/*-
+- ptag = n:eth{src=ethmac, dst=ethmac, type=int, payload=str, ptag=int}
+
+type is optional, defaults to IP
+ptag is optional, defaults to creating a new protocol block
+*/
+static int lnet_eth (lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+
+ const char* src = v_arg_string(L, 2, "src");
+ const char* dst = v_arg_string(L, 2, "dst");
+ int type = v_arg_integer_opt(L, 2, "type", ETHERTYPE_IP);
+ size_t payloadsz = 0;
+ const char* payload = v_arg_lstring(L, 2, "payload", &payloadsz, "");
+ int ptag = lnet_arg_ptag(L, 2);
+
+ if(payloadsz == 0) {
+ payload = NULL;
+ }
+
+#ifdef NET_DUMP
+ printf("net eth src %s dst %s type %d payloadsz %lu ptag %d\n", src, dst, type, payloadsz, ptag);
+#endif
+
+ eth_addr_t src_n = check_eth_pton(L, src, "src");
+ eth_addr_t dst_n = check_eth_pton(L, dst, "dst");
+ ptag = libnet_build_ethernet(dst_n.data, src_n.data, type, (uint8_t*)payload, payloadsz, *ud, ptag);
+ check_error(L, *ud, ptag);
+ lua_pushinteger(L, ptag);
+ return 1;
+}
+
+static int lnet_link (lua_State *L)
+{
+ libnet_t** ud = luaL_checkudata(L, 1, L_NET_REGID);
+ luaL_argcheck(L, *ud, 1, "net has been destroyed");
+ size_t payloadsz = 0;
+ const char* payload = luaL_checklstring(L, 2, &payloadsz);
+
+ int size = libnet_write_link(*ud, (uint8_t*)payload, payloadsz);
+ lua_pushinteger(L, size);
+ return 1;
+}
+
+/*-
+- network = net.pton(presentation)
+
+presentation is something like "df:33:44:12:45:54", or "1.2.3.4", or a host name
+
+return is the binary, network byte-ordered address (you have to know what kind it was!)
+*/
+static int lnet_pton(lua_State *L)
+{
+ const char* src = luaL_checkstring(L, 1);
+ struct addr dst;
+
+ if(addr_pton(src, &dst) < 0) {
+ return luaL_error(L, "pton failed on '%s'", src);
+ }
+
+ int size = dst.addr_bits;
+ void* addr = &dst.__addr_u;
+
+ lua_pushlstring(L, addr, size/8);
+
+ return 1;
+}
+
+/*-
+- chksum = net.chksum(string, ...)
+
+Checksum the series of strings passed in.
+*/
+static int lnet_chksum(lua_State *L)
+{
+ int interm = 0;
+ u_int16_t chks = 0;
+
+ int i;
+ int top = lua_gettop(L);
+ for (i = 1; i <= top; i++) {
+ size_t length = 0;
+ const char* src = luaL_checklstring(L, i, &length);
+ interm += libnet_in_cksum((u_int16_t*)src, length);
+ }
+
+ chks = LIBNET_CKSUM_CARRY(interm);
+
+ lua_pushlstring(L, (char *)&chks, 2);
+ return 1;
+}
+
+/*-
+- remaining = net.nanosleep(seconds)
+
+Seconds can be decimal (resolution is nanoseconds, theoretically).
+Return is number of seconds not slept, or nil and an error message on failure.
+
+remaining = assert(net.nanosleep(seconds))
+
+*/
+static int lnet_nanosleep(lua_State *L)
+{
+ double n = luaL_checknumber(L, 1);
+
+ luaL_argcheck(L, n > 0, 1, "seconds must be greater than zero");
+
+ struct timespec req = { 0 };
+ req.tv_sec = (time_t) n;
+ req.tv_nsec = 1000000000 * (n-req.tv_sec);
+
+ struct timespec rem = { 0 };
+
+ if(nanosleep(&req, &rem) < 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ return 2;
+ } else {
+ lua_pushnumber(L, (double) rem.tv_sec + rem.tv_nsec / 1000000000.0);
+ return 1;
+ }
+}
+
+/*-
+- net.new(injection, device)
+
+injection is one of "link", "raw", ...
+device is "eth0", ...
+*/
+static int lnet_init(lua_State *L)
+{
+ static const char* injection_opt[] = {
+ "link", "link_adv", "raw4", "raw4_adv", "raw6", "raw6_adv", NULL
+ };
+ static int injection_val[] = {
+ LIBNET_LINK, LIBNET_LINK_ADV, LIBNET_RAW4, LIBNET_RAW4_ADV, LIBNET_RAW6, LIBNET_RAW6_ADV
+ };
+ char errbuf[LIBNET_ERRBUF_SIZE];
+ int type = injection_val[luaL_checkoption(L, 1, NULL, injection_opt)];
+ const char *device = luaL_checkstring(L, 2);
+
+ libnet_t** ud = lua_newuserdata(L, sizeof(*ud));
+ *ud = NULL;
+
+ luaL_getmetatable(L, L_NET_REGID);
+ lua_setmetatable(L, -2);
+
+ *ud = libnet_init(type, (char*)device, errbuf);
+
+ if (!*ud) {
+ return luaL_error(L, "%s", errbuf);
+ }
+
+ return 1;
+}
+
+static const luaL_reg net_methods[] =
+{
+ {"__gc", lnet_destroy},
+ {"destroy", lnet_destroy},
+ {"write", lnet_write},
+ {"udp", lnet_udp},
+ {"ipv4", lnet_ipv4},
+ {"eth", lnet_eth},
+ {"write_link", lnet_link},
+ {"block", lnet_block},
+ {"dump", lnet_dump},
+ {NULL, NULL}
+};
+
+static const luaL_reg net[] =
+{
+ {"init", lnet_init},
+ {"pton", lnet_pton},
+ {"checksum", lnet_chksum},
+ {"nanosleep", lnet_nanosleep},
+ {NULL, NULL}
+};
+
+LUALIB_API int luaopen_net (lua_State *L)
+{
+ v_obj_metatable(L, L_NET_REGID, net_methods);
+
+ luaL_register(L, "net", net);
+
+ return 1;
+}
+