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}; |