Software / code / prosody
Comparison
util/ip.lua @ 4419:b1e49cc314cb
util.ip: New module containing IP related functions
| author | Florian Zeitz <florob@babelmonkeys.de> |
|---|---|
| date | Sat, 22 Oct 2011 17:51:53 +0200 |
| child | 4432:cbc3224ed3c2 |
comparison
equal
deleted
inserted
replaced
| 4418:70b5e533325d | 4419:b1e49cc314cb |
|---|---|
| 1 -- Prosody IM | |
| 2 -- Copyright (C) 2008-2011 Florian Zeitz | |
| 3 -- | |
| 4 -- This project is MIT/X11 licensed. Please see the | |
| 5 -- COPYING file in the source package for more information. | |
| 6 -- | |
| 7 | |
| 8 local ip_methods = {}; | |
| 9 local ip_mt = { __index = function (ip, key) return (ip_methods[key])(ip); end, | |
| 10 __tostring = function (ip) return ip.addr; end, | |
| 11 __eq = function (ipA, ipB) return ipA.addr == ipB.addr; end}; | |
| 12 local hex2bits = { ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011", ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111", ["8"] = "1000", ["9"] = "1001", ["A"] = "1010", ["B"] = "1011", ["C"] = "1100", ["D"] = "1101", ["E"] = "1110", ["F"] = "1111" }; | |
| 13 | |
| 14 local function new_ip(ipStr, proto) | |
| 15 if proto ~= "IPv4" and proto ~= "IPv6" then | |
| 16 return nil, "invalid protocol"; | |
| 17 end | |
| 18 | |
| 19 return setmetatable({ addr = ipStr, proto = proto }, ip_mt); | |
| 20 end | |
| 21 | |
| 22 local function toBits(ip) | |
| 23 local result = ""; | |
| 24 local fields = {}; | |
| 25 if ip.proto == "IPv4" then | |
| 26 ip = ip.toV4mapped; | |
| 27 end | |
| 28 ip = (ip.addr):upper(); | |
| 29 ip:gsub("([^:]*):?", function (c) fields[#fields + 1] = c end); | |
| 30 if not ip:match(":$") then fields[#fields] = nil; end | |
| 31 for i, field in ipairs(fields) do | |
| 32 if field:len() == 0 and i ~= 1 and i ~= #fields then | |
| 33 for i = 1, 16 * (9 - #fields) do | |
| 34 result = result .. "0"; | |
| 35 end | |
| 36 else | |
| 37 for i = 1, 4 - field:len() do | |
| 38 result = result .. "0000"; | |
| 39 end | |
| 40 for i = 1, field:len() do | |
| 41 result = result .. hex2bits[field:sub(i,i)]; | |
| 42 end | |
| 43 end | |
| 44 end | |
| 45 return result; | |
| 46 end | |
| 47 | |
| 48 local function commonPrefixLength(ipA, ipB) | |
| 49 ipA, ipB = toBits(ipA), toBits(ipB); | |
| 50 for i = 1, 128 do | |
| 51 if ipA:sub(i,i) ~= ipB:sub(i,i) then | |
| 52 return i-1; | |
| 53 end | |
| 54 end | |
| 55 return 128; | |
| 56 end | |
| 57 | |
| 58 local function v4scope(ip) | |
| 59 local fields = {}; | |
| 60 ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); | |
| 61 -- Loopback: | |
| 62 if fields[1] == 127 then | |
| 63 return 0x2; | |
| 64 -- Link-local unicast: | |
| 65 elseif fields[1] == 169 and fields[2] == 254 then | |
| 66 return 0x2; | |
| 67 -- Site-local unicast: | |
| 68 elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and fields[2] > 16) then | |
| 69 return 0x5; | |
| 70 -- Global unicast: | |
| 71 else | |
| 72 return 0xE; | |
| 73 end | |
| 74 end | |
| 75 | |
| 76 local function v6scope(ip) | |
| 77 -- Loopback: | |
| 78 if ip:match("^[0:]*1$") then | |
| 79 return 0x2; | |
| 80 -- Link-local unicast: | |
| 81 elseif ip:match("^[Ff][Ee][89ABab]") then | |
| 82 return 0x2; | |
| 83 -- Site-local unicast: | |
| 84 elseif ip:match("^[Ff][Ee][CcDdEeFf]") then | |
| 85 return 0x5; | |
| 86 -- Multicast: | |
| 87 elseif ip:match("^[Ff][Ff]") then | |
| 88 return tonumber("0x"..ip:sub(4,4)); | |
| 89 -- Global unicast: | |
| 90 else | |
| 91 return 0xE; | |
| 92 end | |
| 93 end | |
| 94 | |
| 95 local function label(ip) | |
| 96 if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then | |
| 97 return 0; | |
| 98 elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then | |
| 99 return 2; | |
| 100 elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then | |
| 101 return 3; | |
| 102 elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then | |
| 103 return 4; | |
| 104 else | |
| 105 return 1; | |
| 106 end | |
| 107 end | |
| 108 | |
| 109 local function precedence(ip) | |
| 110 if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then | |
| 111 return 50; | |
| 112 elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then | |
| 113 return 30; | |
| 114 elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then | |
| 115 return 20; | |
| 116 elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then | |
| 117 return 10; | |
| 118 else | |
| 119 return 40; | |
| 120 end | |
| 121 end | |
| 122 | |
| 123 local function toV4mapped(ip) | |
| 124 local fields = {}; | |
| 125 local ret = "::ffff:"; | |
| 126 ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); | |
| 127 ret = ret .. ("%02x"):format(fields[1]); | |
| 128 ret = ret .. ("%02x"):format(fields[2]); | |
| 129 ret = ret .. ":" | |
| 130 ret = ret .. ("%02x"):format(fields[3]); | |
| 131 ret = ret .. ("%02x"):format(fields[4]); | |
| 132 return new_ip(ret, "IPv6"); | |
| 133 end | |
| 134 | |
| 135 function ip_methods:toV4mapped() | |
| 136 if self.proto ~= "IPv4" then return nil, "No IPv4 address" end | |
| 137 local value = toV4mapped(self.addr); | |
| 138 self.toV4mapped = value; | |
| 139 return value; | |
| 140 end | |
| 141 | |
| 142 function ip_methods:label() | |
| 143 local value; | |
| 144 if self.proto == "IPv4" then | |
| 145 value = label(self.toV4mapped); | |
| 146 else | |
| 147 value = label(self); | |
| 148 end | |
| 149 self.label = value; | |
| 150 return value; | |
| 151 end | |
| 152 | |
| 153 function ip_methods:precedence() | |
| 154 local value; | |
| 155 if self.proto == "IPv4" then | |
| 156 value = precedence(self.toV4mapped); | |
| 157 else | |
| 158 value = precedence(self); | |
| 159 end | |
| 160 self.precedence = value; | |
| 161 return value; | |
| 162 end | |
| 163 | |
| 164 function ip_methods:scope() | |
| 165 local value; | |
| 166 if self.proto == "IPv4" then | |
| 167 value = v4scope(self.addr); | |
| 168 else | |
| 169 value = v6scope(self.addr); | |
| 170 end | |
| 171 self.scope = value; | |
| 172 return value; | |
| 173 end | |
| 174 | |
| 175 return {new_ip = new_ip, | |
| 176 commonPrefixLength = commonPrefixLength}; |