summaryrefslogtreecommitdiffstats
path: root/aconf/model/net.lua
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-03-10 22:45:18 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2014-03-24 01:18:13 +0200
commit7d9c43916b0600ac4879dfe9793eab807a83ab2b (patch)
treeec54ed64c9a557b6ea4ad88d31138a02d3e0cd04 /aconf/model/net.lua
parentcb6c243dc356ef1d46d7ddb96e6ea6ae007c6cca (diff)
downloadaconf-7d9c43916b0600ac4879dfe9793eab807a83ab2b.tar.bz2
aconf-7d9c43916b0600ac4879dfe9793eab807a83ab2b.tar.xz
rename ACF2 to Alpine Configurator (aconf)
Diffstat (limited to 'aconf/model/net.lua')
-rw-r--r--aconf/model/net.lua203
1 files changed, 203 insertions, 0 deletions
diff --git a/aconf/model/net.lua b/aconf/model/net.lua
new file mode 100644
index 0000000..7d7a00f
--- /dev/null
+++ b/aconf/model/net.lua
@@ -0,0 +1,203 @@
+--[[
+Copyright (c) 2012-2014 Kaarle Ritvanen
+See LICENSE file for license details
+--]]
+
+local M = {}
+
+local raise = require('aconf.error').raise
+local Union = require('aconf.model.combination').Union
+
+local fld = require('aconf.model.field')
+local String = fld.String
+
+local object = require('aconf.object')
+local class = object.class
+local super = object.super
+
+local pth = require('aconf.path')
+local update = require('aconf.util').update
+
+
+local stringy = require('stringy')
+
+
+local BaseIPAddress = class(String)
+
+function BaseIPAddress:abs_mask_addr(context)
+ if self.mask_addr then
+ return pth.join(pth.parent(context.addr), self.mask_addr)
+ end
+end
+
+function BaseIPAddress:topology(context)
+ local res = super(self, BaseIPAddress):topology(context)
+ local maddr = self:abs_mask_addr(context)
+ if maddr then
+ table.insert(res, {path=context.path, addr=maddr, type=self.mask_type})
+ end
+ return res
+end
+
+function BaseIPAddress:invalid(context)
+ raise(context.path, 'Invalid IPv'..self.version..' address')
+end
+
+function BaseIPAddress:split(context, value)
+ local comps = stringy.split(value, '/')
+ if #comps == 1 then return value, self.length end
+
+ if #comps > 2 or not self.cidr then self:invalid(context) end
+
+ local mask = tonumber(comps[2])
+ if not mask or mask < 0 or mask > self.length then self:invalid(context) end
+ return comps[1], mask
+end
+
+function BaseIPAddress:_load(context)
+ local res = super(self, BaseIPAddress):_load(context)
+ local maddr = self:abs_mask_addr(context)
+ if res and maddr then
+ return res..'/'..(self:mask2cidr(context.txn:get(maddr)) or self.length)
+ end
+ return res
+end
+
+function BaseIPAddress:_save(context, value)
+ local maddr = self:abs_mask_addr(context)
+ if maddr then
+ local cidr
+ if value then value, cidr = self:split(context, value) end
+ context.txn:set(maddr, cidr and self:cidr2mask(cidr))
+ end
+ super(self, BaseIPAddress):_save(context, value)
+end
+
+
+M.IPv4Address = class(BaseIPAddress)
+
+function M.IPv4Address:init(params)
+ super(self, M.IPv4Address):init(
+ update(params, {version=4, length=32, mask_type='string'})
+ )
+end
+
+function M.IPv4Address:validate(context, value)
+ super(self, M.IPv4Address):validate(context, value)
+ local address = self:split(context, value)
+
+ local function test(...)
+ if #{...} ~= 4 then return true end
+ for _, octet in ipairs{...} do
+ if tonumber(octet) > 255 then return true end
+ end
+ end
+ if test(address:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then
+ self:invalid(context)
+ end
+end
+
+function M.IPv4Address:mask2cidr(mask)
+ local acc = 0
+ for i, comp in ipairs(stringy.split(mask, '.')) do
+ acc = acc + math.pow(256, 4 - i) * tonumber(comp)
+ end
+ local res = 32
+ while acc % 2 == 0 do
+ res = res - 1
+ assert(res > -1)
+ acc = acc / 2
+ end
+ return res
+end
+
+function M.IPv4Address:cidr2mask(cidr)
+ local acc = (math.pow(2, cidr) - 1) * math.pow(2, 32 - cidr)
+ local comps = {}
+ for i = 4,1,-1 do
+ comps[i] = acc % 256
+ acc = math.floor(acc / 256)
+ end
+ return table.concat(comps, '.')
+end
+
+
+M.IPv6Address = class(BaseIPAddress)
+
+function M.IPv6Address:init(params)
+ super(self, M.IPv6Address):init(
+ update(params, {version=6, length=128, mask_type='number'})
+ )
+end
+
+function M.IPv6Address:validate(context, value)
+ super(self, M.IPv6Address):validate(context, value)
+ local address = self:split(context, value)
+
+ local function invalid() self:invalid(context) end
+
+ if address == '' then invalid() end
+
+ local comps = stringy.split(address, ':')
+ if #comps < 3 then invalid() end
+
+ local function collapse(i, ofs)
+ if comps[i] > '' then return end
+ if comps[i + ofs] > '' then invalid() end
+ table.remove(comps, i)
+ end
+ collapse(1, 1)
+ collapse(#comps, -1)
+ if #comps > 8 then invalid() end
+
+ local short = false
+ for _, comp in ipairs(comps) do
+ if comp == '' then
+ if short then invalid() end
+ short = true
+ elseif not comp:match('^%x%x?%x?%x?$') then invalid() end
+ end
+ if (
+ short and #comps == 3 and comps[2] == ''
+ ) or (not short and #comps < 8) then
+ invalid()
+ end
+end
+
+function M.IPv6Address:mask2cidr(mask) return mask end
+
+function M.IPv6Address:cidr2mask(cidr) return cidr end
+
+
+M.IPAddress = class(Union)
+
+function M.IPAddress:init(params)
+ super(self, M.IPAddress):init(
+ update(
+ params,
+ {types={M.IPv4Address, M.IPv6Address}, error='Invalid IP address'}
+ )
+ )
+end
+
+
+M.Port = class(fld.Integer)
+
+function M.Port:validate(context, value)
+ super(self, M.Port):validate(context, value)
+ if value < 0 or value > 65535 then raise(context.path, 'Invalid port') end
+end
+
+
+M.EmailAddress = class(String)
+
+function M.EmailAddress:init(params)
+ super(self, M.EmailAddress):init(
+ update(
+ params, {pattern='[A-Za-z0-9%.%+%-]+@[A-Za-z0-9%.%+%-]+%.%w%w+'}
+ )
+ )
+end
+
+
+return M