1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 22:21:29 +00:00

I hope this will recover nselib history

This commit is contained in:
fyodor
2007-11-27 22:50:00 +00:00
parent bbb0744761
commit 454e4e3b17
16 changed files with 27674 additions and 0 deletions

27
nselib/Makefile.in Normal file
View File

@@ -0,0 +1,27 @@
PLATFORM=@host@
CC = @CC@
CXX = @CXX@
CCOPT =
DBGFLAGS =
SHTOOL = ../shtool
INSTALL = $(SHTOOL) install
LIBTOOL= ./libtool
LTFLAGS = --tag=CC --silent
all: bit.so
bit.so: bit.c @LIBTOOL_DEPS@
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) @LUAINCLUDE@ $(CFLAGS) -c bit.c
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -avoid-version -module -rpath /usr/local/lib -o bit.la bit.lo
mv .libs/bit.so bit.so
clean:
rm -f bit.so *.la *.lo
rm -rf .libs
distclean: clean
rm -f Makefile config.log config.status libtool

6389
nselib/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

70
nselib/bit.c Normal file
View File

@@ -0,0 +1,70 @@
/* Bitwise operations library
* by Reuben Thomas (rrt@sc3d.org)
* bitlib is a C library for Lua 5.x that provides bitwise operations
* It is copyright Reuben Thomas 2000-2006, and is released under the
* MIT license, like Lua (see http://www.lua.org/copyright.html for the
* full license; it's basically the same as the BSD license). There is no
* warranty.
* the most recent copy can be found at http://rrt.sc3d.org/Software/Lua/
**/
#include "bit.h"
typedef long long Integer;
typedef unsigned long long UInteger;
#define luaL_checkbit(L, n) ((Integer)luaL_checknumber(L, n))
#define luaL_checkubit(L, n) ((UInteger)luaL_checkbit(L, n))
#define TDYADIC(name, op, checkbit1, checkbit2) \
static int bit_ ## name(lua_State* L) { \
lua_pushnumber(L, \
(lua_Number)(checkbit1(L, 1) op checkbit2(L, 2))); \
return 1; \
}
#define DYADIC(name, op) \
TDYADIC(name, op, luaL_checkbit, luaL_checkbit)
#define MONADIC(name, op) \
static int bit_ ## name(lua_State* L) { \
lua_pushnumber(L, (lua_Number)(op luaL_checkbit(L, 1))); \
return 1; \
}
#define VARIADIC(name, op) \
static int bit_ ## name(lua_State *L) { \
int n = lua_gettop(L), i; \
Integer w = luaL_checkbit(L, 1); \
for (i = 2; i <= n; i++) \
w op luaL_checkbit(L, i); \
lua_pushnumber(L, (lua_Number)w); \
return 1; \
}
MONADIC(bnot, ~)
VARIADIC(band, &=)
VARIADIC(bor, |=)
VARIADIC(bxor, ^=)
TDYADIC(lshift, <<, luaL_checkbit, luaL_checkubit)
TDYADIC(rshift, >>, luaL_checkubit, luaL_checkubit)
TDYADIC(arshift, >>, luaL_checkbit, luaL_checkubit)
DYADIC(mod, %)
static const struct luaL_reg bitlib[] = {
{"bnot", bit_bnot},
{"band", bit_band},
{"bor", bit_bor},
{"bxor", bit_bxor},
{"lshift", bit_lshift},
{"rshift", bit_rshift},
{"arshift", bit_arshift},
{"mod", bit_mod},
{NULL, NULL}
};
LUALIB_API int luaopen_bit(lua_State *L) {
luaL_openlib(L, BITLIBNAME, bitlib, 0);
return 1;
}

12
nselib/bit.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef BITLIB
#define BITLIB
#define BITLIBNAME "bit"
#include "lauxlib.h"
#include "lua.h"
LUALIB_API int luaopen_bit(lua_State *L);
#endif

19557
nselib/configure vendored Executable file

File diff suppressed because it is too large Load Diff

23
nselib/configure.ac Normal file
View File

@@ -0,0 +1,23 @@
AC_PREREQ(2.13)
AC_INIT([nselib.h])
AC_PROG_CC
# we want to compile lua modules written in C - which are shared libraries
# therefore disable building static libs - we shouldn't need them
AC_DISABLE_STATIC
AC_LIBTOOL_DLOPEN
AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL)
AC_SUBST(LIBTOOL_DEPS)
AC_CANONICAL_HOST
# needed for lua-includes
AC_CHECK_HEADER([lua.h],,[AC_MSG_NOTICE(using lua-includefiles provided with nmap);[LUAINCLUDE=-I../liblua/]],)
AC_SUBST(LUAINCLUDE)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

32
nselib/ipOps.lua Normal file
View File

@@ -0,0 +1,32 @@
-- See nmaps COPYING for licence
module(...,package.seeall)
-- check to see if ip is part of RFC 1918 address space
isPrivate = function(ip)
local a, b
a, b = get_parts_as_number(ip)
if a == 10 then
return true
elseif a == 172 and (b>15 and b<32) then
return true
elseif a == 192 and b == 168 then
return true
end
return false
end
todword = function(ip)
local a, b, c, d
a,b,c,d = get_parts_as_number(ip)
return (((a*256+b))*256+c)*256+d
end
get_parts_as_number = function(ip)
local a,b,c,d = string.match(ip, "(%d+)%.(%d+)%.(%d+)%.(%d+)")
a = tonumber(a);
b = tonumber(b);
c = tonumber(c);
d = tonumber(d);
return a,b,c,d
end

134
nselib/listop.lua Normal file
View File

@@ -0,0 +1,134 @@
-- See nmaps COPYING for licence
module(..., package.seeall)
--[[
--
Functional programming style 'list' operations
bool is_empty(list)
bool is_list(value)
value apply(function, list)
list map(function, list)
list filter(function, list)
list flatten(list)
list append(list1, list2)
list cons(value1, value2)
list reverse(list)
value car(list)
value ncar(list, x)
list cdr(list)
list ncdr(list, x)
where 'list' is an indexed table
where 'value' is an lua datatype
--]]
-- true if l is empty
function is_empty(l)
return table.getn(l) == 0 and true or false
end
-- true if l is a list
function is_list(l)
return type(l) == 'table' and true or false
end
-- Pass each elements of l to a function f which takes a single
-- argument. All the results are returned in an list
function map(f, l)
local results = {}
for i, v in ipairs(l) do
table.insert(results, f(v))
end
return results
end
-- Pass all elements of l to function f which takes a variable
-- number of arguments or a number of arguments equal to the
-- size of l. The result of f is returned
function apply(f, l)
return f(unpack(l))
end
-- Pass all elements of l to a predicate function f which takes a single
-- argument. All elements where f(l[x]) is true are returned in an
-- indexed list
function filter(f, l)
local results = {}
for i, v in ipairs(l) do
if(f(v)) then
table.insert(results, v)
end
end
return results
end
-- return first element of a list
function car(l)
return l[1]
end
-- return everything but the first element of a list
function cdr(l)
return ncdr(l)
end
-- same as car but start at element x
function ncar(l, x)
x = x or 1
return l[x]
end
-- same as cdr but start at element x
function ncdr(l, x)
local results = {}
x = x or 2
for i = x, table.getn(l) do
results[i-1] = l[i]
end
return results
end
-- prepend a value or list to another value or list
function cons(v1, v2)
return{ is_list(v1) and {unpack(v1)} or v1, is_list(v2) and {unpack(v2)} or v2}
end
-- concatenate two lists and return the result
function append(l1, l2)
local results = {}
for i, v in ipairs(l1) do
table.insert(results, v)
end
for i, v in ipairs(l2) do
table.insert(results, v)
end
return results
end
-- returned l in reverse order
function reverse(l)
local results = {}
for i=table.getn(l), 1, -1 do
table.insert(results, l[i])
end
return results
end
-- return a flat version of nested list l
function flatten(l)
local function flat(r, t)
for i, v in ipairs(t) do
if(type(v) == 'table') then
flat(r, v)
else
table.insert(r, v)
end
end
return r
end
return flat({}, l)
end

31
nselib/match.lua Normal file
View File

@@ -0,0 +1,31 @@
-- See nmaps COPYING for licence
module(..., package.seeall)
require "pcre"
--various functions for use with nse's nsock:receive_buf - function
-- e.g.
-- sock:receivebuf(regex("myregexpattern")) - does a match using pcre- regular-
-- - expressions
-- sock:receivebuf(numbytes(80)) - is the buffered version of
-- sock:receive_bytes(80) - i.e. it returns
-- exactly 80 bytes and no more
regex = function(pattern)
local r = pcre.new(pattern, 0,"C")
return function(buf)
s,e = r:exec(buf, 0,0);
return s,e
end
end
numbytes = function(num)
local n = num
return function(buf)
if(string.len(buf) >=n) then
return n, n
end
return nil
end
end

205
nselib/nse_bitlib.vcproj Normal file
View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="nse_bitlib"
ProjectGUID="{FB7F6FD2-A39D-40A1-86DD-9B08370BDEA6}"
RootNamespace="nse_bitlib"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\liblua"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;NSE_BITLIB_EXPORTS;WIN32;LUA_BUILD_AS_DLL;LUA_LIB"
MinimalRebuild="false"
ExceptionHandling="0"
BasicRuntimeChecks="0"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="2"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="0"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="liblua.lib"
OutputFile=".\bit.dll"
LinkIncremental="2"
AdditionalLibraryDirectories="..\liblua"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="xcopy &quot;$(SolutionDir)..\nselib\*.lua&quot; &quot;$(SolutionDir)\$(ConfigurationName)\nselib\&quot; /y &amp;&amp; xcopy &quot;$(SolutionDir)..\nselib\*.dll&quot; &quot;$(SolutionDir)\$(ConfigurationName)\nselib\&quot; /y"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\liblua"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;NSE_BITLIB_EXPORTS;WIN32;LUA_BUILD_AS_DLL;LUA_LIB"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="liblua.lib"
OutputFile=".\bit.dll"
LinkIncremental="2"
AdditionalLibraryDirectories="..\liblua"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="xcopy &quot;$(SolutionDir)..\nselib\*.lua&quot; &quot;$(SolutionDir)\$(ConfigurationName)\nselib\&quot; /y &amp;&amp; xcopy &quot;$(SolutionDir)..\nselib\*.dll&quot; &quot;$(SolutionDir)\$(ConfigurationName)\nselib\&quot; /y"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\bit.h"
>
</File>
</Filter>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\bit.c"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

8
nselib/nselib.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef NSE_LIB
#define NSE_LIB
#define NSE_BITLIBNAME "bit"
#endif

559
nselib/packet.lua Normal file
View File

@@ -0,0 +1,559 @@
-- license = "See nmaps COPYING for license"
module("packet" ,package.seeall)
-- Raw package parsing functions. Used with raw sockets
-- in nse.
-- Author: Marek Majkowski <majek04+nse@gmail.com>
--[[
--]]
require "bit"
----------------------------------------------------------------------------------------------------------------
-- extract number from binary string
function u8(b, i)
return string.byte(b, i+1)
end
function u16(b, i)
local b1,b2
b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
-- 2^8 2^0
return b1*256 + b2
end
function u32(b,i)
local b1,b2,b3,b4
b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
b3, b4 = string.byte(b, i+3), string.byte(b, i+4)
-- 2^24 2^16 2^8 2^0
return b1*16777216 + b2*65536 + b3*256 + b4
end
-- insert number to binary string
function set_u8(b, i, num)
local s = string.char(bit.band(num, 0xff))
return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1)
end
function set_u16(b, i, num)
local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff))
return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2)
end
function set_u32(b,i, num)
local s = string.char(bit.band(bit.rshift(num,24), 0xff)) ..
string.char(bit.band(bit.rshift(num,16), 0xff)) ..
string.char(bit.band(bit.rshift(num,8), 0xff)) ..
string.char(bit.band(num, 0xff))
return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+4)
end
-- Checksum
---- Standard BSD internet checksum routine check nmap/tcpip.cc
function in_cksum(b)
local sum = 0
local c
local x = b
while x:len() > 1 do
c = x:sub(1,2)
x = x:sub(3)
sum = sum + u16(c, 0)
end
sum = bit.rshift(sum, 16) + bit.band(sum, 0xffff)
sum = sum + bit.rshift(sum, 16)
sum = bit.bnot(sum)
sum = bit.band(sum, 0xffff) -- trunctate to 16 bits
return sum
end
-- ip protocol field
IPPROTO_IP = 0 -- Dummy protocol for TCP
IPPROTO_ICMP = 1 -- Internet Control Message Protocol
IPPROTO_IGMP = 2 -- Internet Group Management Protocol
IPPROTO_IPIP = 4 -- IPIP tunnels (older KA9Q tunnels use 94)
IPPROTO_TCP = 6 -- Transmission Control Protocol
IPPROTO_EGP = 8 -- Exterior Gateway Protocol
IPPROTO_PUP = 12 -- PUP protocol
IPPROTO_UDP = 17 -- User Datagram Protocol
IPPROTO_IDP = 22 -- XNS IDP protocol
IPPROTO_DCCP = 33 -- Datagram Congestion Control Protocol
IPPROTO_RSVP = 46 -- RSVP protocol
IPPROTO_GRE = 47 -- Cisco GRE tunnels (rfc 1701,1702)
IPPROTO_IPV6 = 41 -- IPv6-in-IPv4 tunnelling
IPPROTO_ESP = 50 -- Encapsulation Security Payload protocol
IPPROTO_AH = 51 -- Authentication Header protocol
IPPROTO_BEETPH = 94 -- IP option pseudo header for BEET
IPPROTO_PIM = 103 -- Protocol Independent Multicast
IPPROTO_COMP = 108 -- Compression Header protocol
IPPROTO_SCTP = 132 -- Stream Control Transport Protocol
IPPROTO_UDPLITE = 136 -- UDP-Lite (RFC 3828)
----------------------------------------------------------------------------------------------------------------
-- Packet is a class
Packet = {}
-- Constructor
-- packet - binary string with packet data
-- packet_len - packet length, it could be more than string.len(packet)
-- force_continue - whether error in parsing headers should be fatal or not.
-- especially usefull at parsing icmp packets, where on small icmp payload
-- could be tcp header. The problem is that parsing this payload normally
-- would fail (broken packet, because tcp header is too small)
-- The basic question is if too short tcp header should be treated as fatal error.
function Packet:new(packet, packet_len, force_continue)
local o = setmetatable({}, {__index = Packet})
o.buf = packet
o.packet_len = packet_len
if not o:ip_parse(force_continue) then
return nil
end
if o.ip_p == IPPROTO_TCP then
if not o:tcp_parse(force_continue) then
io.write("Error while parsing TCP packet\n")
end
elseif o.ip_p == IPPROTO_ICMP then
if not o:icmp_parse(force_continue) then
io.write("Error while parsing ICMP packet\n")
end
end
return o
end
-- Helpers
-- from ip notation as string (like 1.2.3.4) to raw_string(4 bytes long)
function iptobin(str)
local ret = ""
for c in string.gmatch(str, "[0-9]+") do
ret = ret .. string.char(c+0) -- automatic conversion to int
end
return ret
end
-- from raw_ip (four bytes string) to dot-notation (like 1.2.3.4)
function toip(raw_ip_addr)
if not raw_ip_addr then
return "?.?.?.?"
end
return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4))
end
-- get unsigned byte
function Packet:u8(index)
return u8(self.buf, index)
end
function Packet:u16(index)
return u16(self.buf, index)
end
function Packet:u32(index)
return u32(self.buf, index)
end
function Packet:raw(index, length)
return string.char(string.byte(self.buf, index+1, index+1+length-1))
end
function Packet:set_u8(index, num)
self.buf = set_u8(self.buf, index, num)
return self.buf
end
function Packet:set_u16(index, num)
self.buf = set_u16(self.buf, index, num)
return self.buf
end
function Packet:set_u32(index, num)
self.buf = set_u32(self.buf, index, num)
return self.buf
end
-- PARSE IP PACKET HEADER
function Packet:ip_parse(force_continue)
self.ip_offset = 0
if string.len(self.buf) < 20 then -- too short
return false
end
self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4)
self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset
if self.ip_v ~= 4 then -- not ip
return false
end
self.ip = true
self.ip_tos = self:u8(self.ip_offset + 1)
self.ip_len = self:u16(self.ip_offset + 2)
self.ip_id = self:u16(self.ip_offset + 4)
self.ip_off = self:u16(self.ip_offset + 6)
self.ip_rf = bit.band(self.ip_off, 0x8000)~=0 -- true/false
self.ip_df = bit.band(self.ip_off, 0x4000)~=0
self.ip_mf = bit.band(self.ip_off, 0x2000)~=0
self.ip_off = bit.band(self.ip_off, 0x1FFF) -- fragment offset
self.ip_ttl = self:u8(self.ip_offset + 8)
self.ip_p = self:u8(self.ip_offset + 9)
self.ip_sum = self:u16(self.ip_offset + 10)
self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string
self.ip_bin_dst = self:raw(self.ip_offset + 16,4)
self.ip_src = toip(self.ip_bin_src) -- formatted string
self.ip_dst = toip(self.ip_bin_dst)
self.ip_opt_offset = self.ip_offset + 20
self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20))
self.ip_data_offset = self.ip_offset + self.ip_hl*4
return true
end
-- set header length field
function Packet:ip_set_hl(len)
self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F)))
self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4)
self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset
end
-- set packet length field
function Packet:ip_set_len(len)
self:set_u16(self.ip_offset + 2, len)
end
-- set ttl
function Packet:ip_set_ttl(ttl)
self:set_u8(self.ip_offset + 8, ttl)
end
-- set checksum
function Packet:ip_set_checksum(checksum)
self:set_u16(self.ip_offset + 10, checksum)
end
-- count checksum for packet and save it
function Packet:ip_count_checksum()
self:ip_set_checksum(0)
local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4) )
self:ip_set_checksum(csum)
end
-- set source ip
function Packet:ip_set_bin_src(binip)
nrip = u32(binip, 0)
self:set_u32(self.ip_offset + 12, nrip)
self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string
end
-- set destination ip
function Packet:ip_set_bin_dst(binip)
nrip = u32(binip, 0)
self:set_u32(self.ip_offset + 16, nrip)
self.ip_bin_dst = self:raw(self.ip_offset + 16,4)
end
-- set ip options field (and move the data, count new length etc)
function Packet:ip_set_options(ipoptions)
-- packet = <ip header> + ipoptions + <payload>
local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1)
self.buf = buf
-- set ip_len
self:ip_set_len(self.buf:len())
-- set ip_hl
self:ip_set_hl(5 + ipoptions:len()/4)
-- set data offset correctly
self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20))
self.ip_data_offset = self.ip_offset + self.ip_hl*4
if self.tcp then
self.tcp_offset = self.ip_data_offset
elseif self.icmp then
self.icmp_offset = self.ip_data_offset
end
end
-- return short information about ip header
function Packet:ip_tostring()
return string.format(
"IP %s -> %s",
self.ip_src,
self.ip_dst)
end
-- parse ip/tcp options to dict structure
function Packet:parse_options(offset, length)
local options = {}
local op = 1
local opt_ptr = 0
while opt_ptr < length do
local t, l, d
options[op] = {}
t = self:u8(offset + opt_ptr)
options[op].type = t
if t==0 or t==1 then
l = 1
d = nil
else
l = self:u8(offset + opt_ptr + 1)
if l > 2 then
d = self:raw(offset + opt_ptr + 2, l-2)
end
end
options[op].len = l
options[op].data = d
opt_ptr = opt_ptr + l
op = op + 1
end
return options
end
-- print short information about current packet
function Packet:tostring()
if self.tcp then
return self:tcp_tostring()
elseif self.icmp then
return self:icmp_tostring()
elseif self.ip then
return self:ip_tostring()
end
return "<no tostring!>"
end
----------------------------------------------------------------------------------------------------------------
-- PARSE ICMP PACKET HEADER
function Packet:icmp_parse(force_continue)
self.icmp_offset = self.ip_data_offset
if string.len(self.buf) < self.icmp_offset + 8 then -- let's say 8 bytes minimum
return false
end
self.icmp = true
self.icmp_type = self:u8(self.icmp_offset + 0)
self.icmp_code = self:u8(self.icmp_offset + 1)
self.icmp_sum = self:u16(self.icmp_offset + 2)
if self.icmp_type == 3 or self.icmp_type == 4 or self.icmp_type == 11 or self.icmp_type == 12 then
self.icmp_payload = true
self.icmp_r0 = self:u32(self.icmp_offset + 4)
self.icmp_payload_offset = self.icmp_offset + 8
if string.len(self.buf) < self.icmp_payload_offset + 24 then
return false
end
self.icmp_payload = Packet:new(self.buf:sub(self.icmp_payload_offset+1), self.packet_len - self.icmp_payload_offset, true)
end
return true
end
-- return short information about icmp header
function Packet:icmp_tostring()
return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")"
end
----------------------------------------------------------------------------------------------------------------
-- PARSE TCP HEADER FROM PACKET
function Packet:tcp_parse(force_continue)
self.tcp = true
self.tcp_offset = self.ip_data_offset
if string.len(self.buf) < self.tcp_offset + 4 then
return false
end
self.tcp_sport = self:u16(self.tcp_offset + 0)
self.tcp_dport = self:u16(self.tcp_offset + 2)
if string.len(self.buf) < self.tcp_offset + 20 then
if force_continue then
return true
else
return false
end
end
self.tcp_seq = self:u32(self.tcp_offset + 4)
self.tcp_ack = self:u32(self.tcp_offset + 8)
self.tcp_hl = bit.rshift(bit.band(self:u8(self.tcp_offset+12), 0xF0), 4) -- header_length or data_offset
self.tcp_x2 = bit.band(self:u8(self.tcp_offset+12), 0x0F)
self.tcp_flags = self:u8(self.tcp_offset + 13)
self.tcp_th_fin = bit.band(self.tcp_flags, 0x01)~=0 -- true/false
self.tcp_th_syn = bit.band(self.tcp_flags, 0x02)~=0
self.tcp_th_rst = bit.band(self.tcp_flags, 0x04)~=0
self.tcp_th_push = bit.band(self.tcp_flags, 0x08)~=0
self.tcp_th_ack = bit.band(self.tcp_flags, 0x10)~=0
self.tcp_th_urg = bit.band(self.tcp_flags, 0x20)~=0
self.tcp_th_ece = bit.band(self.tcp_flags, 0x40)~=0
self.tcp_th_cwr = bit.band(self.tcp_flags, 0x80)~=0
self.tcp_win = self:u16(self.tcp_offset + 14)
self.tcp_sum = self:u16(self.tcp_offset + 16)
self.tcp_urp = self:u16(self.tcp_offset + 18)
self.tcp_opt_offset = self.tcp_offset + 20
self.tcp_options = self:parse_options(self.tcp_opt_offset, ((self.tcp_hl*4)-20))
self.tcp_data_offset = self.tcp_offset + self.tcp_hl*4
self.tcp_data_length = self.ip_len - self.tcp_offset - self.tcp_hl*4
self:tcp_parse_options()
return true
end
-- return short information about tcp packet
function Packet:tcp_tostring()
return string.format(
"TCP %s:%i -> %s:%i",
self.ip_src, self.tcp_sport,
self.ip_dst, self.tcp_dport
)
end
-- parse options for tcp header
function Packet:tcp_parse_options()
local eoo = false
for _,opt in ipairs(self.tcp_options) do
if eoo then
self.tcp_opt_after_eol = true
end
if opt.type == 0 then -- end of options
eoo = true
elseif opt.type == 2 then -- MSS
self.tcp_opt_mss = u16(opt.data, 0)
self.tcp_opt_mtu = self.tcp_opt_mss + 40
elseif opt.type == 3 then -- widow scaling
self.tcp_opt_ws = u8(opt.data, 0)
elseif opt.type == 8 then -- timestamp
self.tcp_opt_t1 = u32(opt.data, 0)
self.tcp_opt_t2 = u32(opt.data, 4)
end
end
end
function Packet:tcp_set_sport(port)
self:set_u16(self.tcp_offset + 0, port)
end
function Packet:tcp_set_dport(port)
self:set_u16(self.tcp_offset + 2, port)
end
-- set tcp sequence field
function Packet:tcp_set_seq(new_seq)
self:set_u32(self.tcp_offset + 4, new_seq)
end
-- set tcp flags field (like syn, ack, rst)
function Packet:tcp_set_flags(new_flags)
self:set_u8(self.tcp_offset + 13, new_flags)
end
-- set urgent pointer field
function Packet:tcp_set_urp(urg_ptr)
self:set_u16(self.tcp_offset + 18, urg_ptr)
end
-- set tcp checksum field
function Packet:tcp_set_checksum(checksum)
self:set_u16(self.tcp_offset + 16, checksum)
end
-- count and save tcp checksum field
function Packet:tcp_count_checksum()
self:tcp_set_checksum(0)
local proto = self.ip_p
local length = self.buf:len() - self.tcp_offset
local b = self.ip_bin_src ..
self.ip_bin_dst ..
string.char(0) ..
string.char(proto) ..
set_u16("..", 0, length) ..
self.buf:sub(self.tcp_offset+1)
self:tcp_set_checksum(in_cksum(b))
end
-- small database, mtu to link type string. Stolen from p0f.
function Packet:tcp_lookup_link()
local mtu_def = {
{["mtu"]=256, ["txt"]= "radio modem"},
{["mtu"]=386, ["txt"]= "ethernut"},
{["mtu"]=552, ["txt"]= "SLIP line / encap ppp"},
{["mtu"]=576, ["txt"]= "sometimes modem"},
{["mtu"]=1280, ["txt"]= "gif tunnel"},
{["mtu"]=1300, ["txt"]= "PIX, SMC, sometimes wireless"},
{["mtu"]=1362, ["txt"]= "sometimes DSL (1)"},
{["mtu"]=1372, ["txt"]= "cable modem"},
{["mtu"]=1400, ["txt"]= "(Google/AOL)"},
{["mtu"]=1415, ["txt"]= "sometimes wireless"},
{["mtu"]=1420, ["txt"]= "GPRS, T1, FreeS/WAN"},
{["mtu"]=1423, ["txt"]= "sometimes cable"},
{["mtu"]=1440, ["txt"]= "sometimes DSL (2)"},
{["mtu"]=1442, ["txt"]= "IPIP tunnel"},
{["mtu"]=1450, ["txt"]= "vtun"},
{["mtu"]=1452, ["txt"]= "sometimes DSL (3)"},
{["mtu"]=1454, ["txt"]= "sometimes DSL (4)"},
{["mtu"]=1456, ["txt"]= "ISDN ppp"},
{["mtu"]=1458, ["txt"]= "BT DSL (?)"},
{["mtu"]=1462, ["txt"]= "sometimes DSL (5)"},
{["mtu"]=1470, ["txt"]= "(Google 2)"},
{["mtu"]=1476, ["txt"]= "IPSec/GRE"},
{["mtu"]=1480, ["txt"]= "IPv6/IPIP"},
{["mtu"]=1492, ["txt"]= "pppoe (DSL)"},
{["mtu"]=1496, ["txt"]= "vLAN"},
{["mtu"]=1500, ["txt"]= "ethernet/modem"},
{["mtu"]=1656, ["txt"]= "Ericsson HIS"},
{["mtu"]=2024, ["txt"]= "wireless/IrDA"},
{["mtu"]=2048, ["txt"]= "Cyclom X.25 WAN"},
{["mtu"]=2250, ["txt"]= "AiroNet wireless"},
{["mtu"]=3924, ["txt"]= "loopback"},
{["mtu"]=4056, ["txt"]= "token ring (1)"},
{["mtu"]=4096, ["txt"]= "Sangoma X.25 WAN"},
{["mtu"]=4352, ["txt"]= "FDDI"},
{["mtu"]=4500, ["txt"]= "token ring (2)"},
{["mtu"]=9180, ["txt"]= "FORE ATM"},
{["mtu"]=16384, ["txt"]= "sometimes loopback (1)"},
{["mtu"]=16436, ["txt"]= "sometimes loopback (2)"},
{["mtu"]=18000, ["txt"]= "token ring x4"},
}
if not self.tcp_opt_mss or self.tcp_opt_mss==0 then
return "unspecified"
end
for _,x in ipairs(mtu_def) do
local mtu = x["mtu"]
local txt = x["txt"]
if self.tcp_opt_mtu == mtu then
return txt
end
if self.tcp_opt_mtu < mtu then
return string.format("unknown-%i", self.tcp_opt_mtu)
end
end
return string.format("unknown-%i", self.tcp_opt_mtu)
end
----------------------------------------------------------------------------------------------------------------
-- UTILS
-- get binary string as hex string
function bintohex(str)
local b = ""
if not str then -- nil
return ""
end
for c in string.gmatch(str, ".") do
b = string.format('%s%02x',b, string.byte(c))
end
return b
end
-- Parse specifically printed hex string as binary
-- Only bytes [a-f0-9A-F] from input are interpreted. The rest is ignored.
-- Number of interpreted bytes _must_ be even. *The input is interpreted in pairs*.
-- hextobin("20 20 20") -> " "
-- hextobin("414243") -> "ABC"
-- hextobin("\\41\\42\\43") -> "ABC"
-- hextobin(" 41 42 43 ")-> "ABC"
function hextobin(str)
local ret = ""
local a,b
if not str then -- nil
return ""
end
for c in string.gmatch(str, "[0-9a-fA-F][0-9a-fA-F]") do
a = string.byte(c:sub(1,1))
b = string.byte(c:sub(2,2))
if a >= string.byte('a') then -- 97>a-f
a = a - string.byte('a') + 10
elseif a >= string.byte('A') then -- 65>A-F
a = a - string.byte('A') + 10
else -- 48> 0-9
a = a - string.byte('0')
end
if b >= string.byte('a') then -- 97>a-f
b = b - string.byte('a') + 10
elseif b >= string.byte('A') then -- 65>A-F
b = b - string.byte('A') + 10
else -- 48> 0-9
b = b - string.byte('0')
end
--io.write(string.format(">%s %i %i\n",c, a, b))
ret = ret .. string.char(a*16 + b)
end
--io.write(string.format(">%s|%s<\n",bintohex(ret), str))
return ret
end

81
nselib/shortport.lua Normal file
View File

@@ -0,0 +1,81 @@
-- See nmaps COPYING for licence
module(..., package.seeall)
portnumber = function(port, _proto, _state)
local port_table, state_table
local proto = _proto or "tcp"
local state = _state or {"open"}
if(type(port) == "number") then
port_table = {port}
elseif(type(port) == "table") then
port_table = port
end
if(type(state) == "string") then
state_table = {state}
elseif(type(state) == "table") then
state_table = state
end
return function(host, port)
for _, state in pairs(state_table) do
if(port.protocol == proto and port.state == state) then
for _, _port in ipairs(port_table) do
if(port.number == _port) then
return true
end
end
end
end
return false
end
end
service = function(service, _proto, _state)
local service_table;
local state = _state or "open"
local proto = _proto or "tcp"
if(type(service) == "string") then
service_table = {service}
elseif(type(service) == "table") then
service_table = service
end
return function(host, port)
if(port.protocol == proto and port.state == state) then
for _, service in ipairs(service_table) do
if(port.service == service) then
return true
end
end
end
return false
end
end
port_or_service = function(port, _service, proto, _state)
local state = _state or {"open"}
local state_table
if(type(state) == "string") then
state_table = {state}
elseif(type(state) == "table") then
state_table = state
end
return function(host, port)
for _, state in pairs(state_table) do
local port_checker = portnumber(port, proto, state)
local service_checker = service(_service, proto, state)
if (port_checker(host, port) or service_checker(host, port)) then
return true
end
end
return false
end
end

106
nselib/stdnse.lua Normal file
View File

@@ -0,0 +1,106 @@
-- See nmaps COPYING for licence
module(..., package.seeall)
print_debug = function(...)
local verbosity = 1;
if ((#arg > 1) and (tonumber(arg[1]))) then
verbosity = table.remove(arg, 1);
end
nmap.print_debug_unformatted(verbosity, string.format(unpack(arg, start)));
end
-- Concat the contents of the parameter list,
-- separated by the string delimiter (just like in perl)
-- example: strjoin(", ", {"Anna", "Bob", "Charlie", "Dolores"})
function strjoin(delimiter, list)
return table.concat(list, delimiter);
end
-- Split text into a list consisting of the strings in text,
-- separated by strings matching delimiter (which may be a pattern).
-- example: strsplit(",%s*", "Anna, Bob, Charlie,Dolores")
function strsplit(delimiter, text)
local list = {}
local pos = 1
if string.find("", delimiter, 1) then -- this would result in endless loops
error("delimiter matches empty string!")
end
while 1 do
local first, last = string.find(text, delimiter, pos)
if first then -- found?
table.insert(list, string.sub(text, pos, first-1))
pos = last+1
else
table.insert(list, string.sub(text, pos))
break
end
end
return list
end
-- Generic buffer implementation using lexical closures
--
-- Pass make_buffer a socket and a separator lua pattern [1].
--
-- Returns a function bound to your provided socket with behaviour identical
-- to receive_lines() except it will return AT LEAST ONE [2] and AT MOST ONE
-- "line" at a time.
--
-- [1] Use the pattern "\r?\n" for regular newlines
-- [2] Except where there is trailing "left over" data not terminated by a
-- pattern (in which case you get the data anyways)
-- [3] The data is returned WITHOUT the pattern/newline on the end.
-- [4] Empty "lines" are returned as "". With the pattern in [1] you will
-- receive a "" for each newline in the stream.
-- [5] Errors/EOFs are delayed until all "lines" have been processed.
--
-- -Doug, June, 2007
make_buffer = function(sd, sep)
local self, result
local buf = ""
self = function()
local i, j, status, value
i, j = string.find(buf, sep)
if i then
if i == 1 then -- empty line
buf = string.sub(buf, j+1, -1)
--return self() -- skip empty, tail
return true, "" -- return empty
else
value = string.sub(buf, 1, i-1)
buf = string.sub(buf, j+1, -1)
return true, value
end
end
if result then
if string.len(buf) > 0 then -- left over data with no terminating pattern
value = buf
buf = ""
return true, value
end
return nil, result
end
status, value = sd:receive()
if status then
buf = buf .. value
else
result = value
end
return self() -- tail
end
return self
end

84
nselib/strbuf.lua Normal file
View File

@@ -0,0 +1,84 @@
-- license = "See nmaps COPYING for license"
module("strbuf" ,package.seeall)
-- String buffer functions. Concatenation is not efficient in
-- lua as strings are immutable. If a large amount of '..'
-- operations are needed a string buffer should be used instead
--[[
local buf = strbuf.new()
-- from here buf may be used like a string for concatenation operations
-- (the lefthand-operand has to be a strbuf, the righthand-operand may be
-- a string or a strbuf)
-- alternativly you can assign a value (which will become the first string
-- inside the buffer) with new
local buf2 = strbuf.new('hello')
buf = buf .. 'string'
buf = buf .. 'data'
print(buf) -- default seperator is a new line
print(strbuf.dump(buf)) -- no seperator
print(strbuf.dump(buf, ' ')) -- seperated by spaces
strbuf.clear(buf)
--]]
dump = table.concat
concatbuf =function(sbuf, s)
if sbuf == s then
error("refusing to concat the same buffer (recursion)!")
end
if getmetatable(sbuf) ~= mt then
error("left-hand operand of the concat operation has to be a strbuf!")
end
if type(s)=="string" then
table.insert(sbuf, s)
elseif getmetatable(s) == mt then
for _,v in ipairs(s) do
table.insert(sbuf, v)
end
else
error("right-hand operand of concat has to be either string or strbuf!")
end
return sbuf
end
local eqbuf = function(sbuf1, sbuf2)
if getmetatable(sbuf1) ~= mt then
error("equal function expects a value of type strbuf as left-hand operand")
end
if getmetatable(sbuf1) ~= getmetatable(sbuf2) then
return false
end
if #sbuf1 ~= #sbuf2 then
return false
end
for i=1, #sbuf1 do
if sbuf1[i] ~= sbuf2[i] then
return false
end
end
return true
end
clear = function(sbuf)
for i, v in pairs(sbuf) do
sbuf[i] = nil
end
end
mt = { __concat = concatbuf, __tostring = function(s) return dump(s, '\n') end , __eq=eqbuf}
new = function(val)
local tmp ={}
setmetatable(tmp, mt)
if val ~=nil then
table.insert(tmp, val)
end
return tmp
end

356
nselib/url.lua Normal file
View File

@@ -0,0 +1,356 @@
--[[
URI parsing, composition and relative URL resolution
LuaSocket toolkit.
Author: Diego Nehab
RCS ID: $Id: url.lua,v 1.37 2005/11/22 08:33:29 diego Exp $
parse_query() and build_query() added For nmap (Eddie Bell <ejlbell@gmail.com>)
--]]
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local base = _G
local table = require("table")
module(...,package.seeall)
_VERSION = "URL 1.0"
--[[ Internal functions --]]
-----------------------------------------------------------------------------
-- Protects a path segment, to prevent it from interfering with the
-- url parsing.
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
local function make_set(t)
local s = {}
for i,v in base.ipairs(t) do
s[t[i]] = 1
end
return s
end
-- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",",
}
local function protect_segment(s)
return string.gsub(s, "([^A-Za-z0-9_])", function (c)
if segment_set[c] then return c
else return string.format("%%%02x", string.byte(c)) end
end)
end
-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
-- base_path
-- relative_path
-- Returns
-- corresponding absolute path
-----------------------------------------------------------------------------
local function absolute_path(base_path, relative_path)
if string.sub(relative_path, 1, 1) == "/" then return relative_path end
local path = string.gsub(base_path, "[^/]*$", "")
path = path .. relative_path
path = string.gsub(path, "([^/]*%./)", function (s)
if s ~= "./" then return s else return "" end
end)
path = string.gsub(path, "/%.$", "/")
local reduced
while reduced ~= path do
reduced = path
path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
if s ~= "../../" then return "" else return s end
end)
end
path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
if s ~= "../.." then return "" else return s end
end)
return path
end
--[[ External functions --]]
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function escape(s)
return string.gsub(s, "([^A-Za-z0-9_])", function(c)
return string.format("%%%02x", string.byte(c))
end)
end
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function unescape(s)
return string.gsub(s, "%%(%x%x)", function(hex)
return string.char(base.tonumber(hex, 16))
end)
end
-----------------------------------------------------------------------------
-- Parses a url and returns a table with all its parts according to RFC 2396
-- The following grammar describes the names given to the URL parts
-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
-- <authority> ::= <userinfo>@<host>:<port>
-- <userinfo> ::= <user>[:<password>]
-- <path> :: = {<segment>/}<segment>
-- Input
-- url: uniform resource locator of request
-- default: table with default values for each field
-- Returns
-- table with the following fields, where RFC naming conventions have
-- been preserved:
-- scheme, authority, userinfo, user, password, host, port,
-- path, params, query, fragment
-- Obs:
-- the leading '/' in {/<path>} is considered part of <path>
-----------------------------------------------------------------------------
function parse(url, default)
-- initialize default parameters
local parsed = {}
for i,v in base.pairs(default or parsed) do parsed[i] = v end
-- empty url is parsed to nil
if not url or url == "" then return nil, "invalid url" end
-- remove whitespace
-- url = string.gsub(url, "%s", "")
-- get fragment
url = string.gsub(url, "#(.*)$", function(f)
parsed.fragment = f
return ""
end)
-- get scheme
url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
function(s) parsed.scheme = s; return "" end)
-- get authority
url = string.gsub(url, "^//([^/]*)", function(n)
parsed.authority = n
return ""
end)
-- get query stringing
url = string.gsub(url, "%?(.*)", function(q)
parsed.query = q
return ""
end)
-- get params
url = string.gsub(url, "%;(.*)", function(p)
parsed.params = p
return ""
end)
-- path is whatever was left
if url ~= "" then parsed.path = url end
local authority = parsed.authority
if not authority then return parsed end
authority = string.gsub(authority,"^([^@]*)@",
function(u) parsed.userinfo = u; return "" end)
authority = string.gsub(authority, ":([^:]*)$",
function(p) parsed.port = p; return "" end)
if authority ~= "" then parsed.host = authority end
local userinfo = parsed.userinfo
if not userinfo then return parsed end
userinfo = string.gsub(userinfo, ":([^:]*)$",
function(p) parsed.password = p; return "" end)
parsed.user = userinfo
return parsed
end
-----------------------------------------------------------------------------
-- Rebuilds a parsed URL from its components.
-- Components are protected if any reserved or unallowed characters are found
-- Input
-- parsed: parsed URL, as returned by parse
-- Returns
-- a stringing with the corresponding URL
-----------------------------------------------------------------------------
function build(parsed)
local ppath = parse_path(parsed.path or "")
local url = build_path(ppath)
if parsed.params then url = url .. ";" .. parsed.params end
if parsed.query then url = url .. "?" .. parsed.query end
local authority = parsed.authority
if parsed.host then
authority = parsed.host
if parsed.port then authority = authority .. ":" .. parsed.port end
local userinfo = parsed.userinfo
if parsed.user then
userinfo = parsed.user
if parsed.password then
userinfo = userinfo .. ":" .. parsed.password
end
end
if userinfo then authority = userinfo .. "@" .. authority end
end
if authority then url = "//" .. authority .. url end
if parsed.scheme then url = parsed.scheme .. ":" .. url end
if parsed.fragment then url = url .. "#" .. parsed.fragment end
-- url = string.gsub(url, "%s", "")
return url
end
-----------------------------------------------------------------------------
-- Builds a absolute URL from a base and a relative URL according to RFC 2396
-- Input
-- base_url
-- relative_url
-- Returns
-- corresponding absolute url
-----------------------------------------------------------------------------
function absolute(base_url, relative_url)
if type(base_url) == "table" then
base_parsed = base_url
base_url = build(base_parsed)
else
base_parsed = parse(base_url)
end
local relative_parsed = parse(relative_url)
if not base_parsed then return relative_url
elseif not relative_parsed then return base_url
elseif relative_parsed.scheme then return relative_url
else
relative_parsed.scheme = base_parsed.scheme
if not relative_parsed.authority then
relative_parsed.authority = base_parsed.authority
if not relative_parsed.path then
relative_parsed.path = base_parsed.path
if not relative_parsed.params then
relative_parsed.params = base_parsed.params
if not relative_parsed.query then
relative_parsed.query = base_parsed.query
end
end
else
relative_parsed.path = absolute_path(base_parsed.path or "",
relative_parsed.path)
end
end
return build(relative_parsed)
end
end
-----------------------------------------------------------------------------
-- Breaks a path into its segments, unescaping the segments
-- Input
-- path
-- Returns
-- segment: a table with one entry per segment
-----------------------------------------------------------------------------
function parse_path(path)
local parsed = {}
path = path or ""
--path = string.gsub(path, "%s", "")
string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
for i = 1, table.getn(parsed) do
parsed[i] = unescape(parsed[i])
end
if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
return parsed
end
-----------------------------------------------------------------------------
-- Builds a path component from its segments, escaping protected characters.
-- Input
-- parsed: path segments
-- unsafe: if true, segments are not protected before path is built
-- Returns
-- path: corresponding path stringing
-----------------------------------------------------------------------------
function build_path(parsed, unsafe)
local path = ""
local n = table.getn(parsed)
if unsafe then
for i = 1, n-1 do
path = path .. parsed[i]
path = path .. "/"
end
if n > 0 then
path = path .. parsed[n]
if parsed.is_directory then path = path .. "/" end
end
else
for i = 1, n-1 do
path = path .. protect_segment(parsed[i])
path = path .. "/"
end
if n > 0 then
path = path .. protect_segment(parsed[n])
if parsed.is_directory then path = path .. "/" end
end
end
if parsed.is_absolute then path = "/" .. path end
return path
end
-----------------------------------------------------------------------------
-- Breaks a query string into name/value pairs
-- Input
-- query string (name=value&name=value ...)
-- Returns
-- table where name=value is table['name'] = value
-----------------------------------------------------------------------------
function parse_query(query)
local parsed = {}
local pos = 0
query = string.gsub(query, "&amp;", "&")
query = string.gsub(query, "&lt;", "<")
query = string.gsub(query, "&gt;", ">")
function ginsert(qstr)
local first, last = string.find(qstr, "=")
if first then
parsed[string.sub(qstr, 0, first-1)] = string.sub(qstr, first+1)
end
end
while true do
local first, last = string.find(query, "&", pos)
if first then
ginsert(string.sub(query, pos, first-1));
pos = last+1
else
ginsert(string.sub(query, pos));
break;
end
end
return parsed
end
-----------------------------------------------------------------------------
-- Builds a query string from dictionary based table
-- Input
-- dictionary table where table['name'] = value
-- Returns
-- query string (name=value&name=value ...)
-----------------------------------------------------------------------------
function build_query(query)
local qstr = ""
for i,v in pairs(query) do
qstr = qstr .. i .. '=' .. v .. '&'
end
return string.sub(qstr, 0, string.len(qstr)-1)
end