diff options
author | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2014-03-10 22:45:18 +0200 |
---|---|---|
committer | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2014-03-24 01:18:13 +0200 |
commit | 7d9c43916b0600ac4879dfe9793eab807a83ab2b (patch) | |
tree | ec54ed64c9a557b6ea4ad88d31138a02d3e0cd04 /aconf/model/net.lua | |
parent | cb6c243dc356ef1d46d7ddb96e6ea6ae007c6cca (diff) | |
download | aconf-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.lua | 203 |
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 |