Merge 0.10->trunk
[prosody.git] / util / rfc6724.lua
1 -- Prosody IM
2 -- Copyright (C) 2011-2013 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 -- This is used to sort destination addresses by preference
9 -- during S2S connections.
10 -- We can't hand this off to getaddrinfo, since it blocks
11
12 local ip_commonPrefixLength = require"util.ip".commonPrefixLength
13
14 local function commonPrefixLength(ipA, ipB)
15         local len = ip_commonPrefixLength(ipA, ipB);
16         return len < 64 and len or 64;
17 end
18
19 local function t_sort(t, comp)
20         for i = 1, (#t - 1) do
21                 for j = (i + 1), #t do
22                         local a, b = t[i], t[j];
23                         if not comp(a,b) then
24                                 t[i], t[j] = b, a;
25                         end
26                 end
27         end
28 end
29
30 local function source(dest, candidates)
31         local function comp(ipA, ipB)
32                 -- Rule 1: Prefer same address
33                 if dest == ipA then
34                         return true;
35                 elseif dest == ipB then
36                         return false;
37                 end
38
39                 -- Rule 2: Prefer appropriate scope
40                 if ipA.scope < ipB.scope then
41                         if ipA.scope < dest.scope then
42                                 return false;
43                         else
44                                 return true;
45                         end
46                 elseif ipA.scope > ipB.scope then
47                         if ipB.scope < dest.scope then
48                                 return true;
49                         else
50                                 return false;
51                         end
52                 end
53
54                 -- Rule 3: Avoid deprecated addresses
55                 -- XXX: No way to determine this
56                 -- Rule 4: Prefer home addresses
57                 -- XXX: Mobility Address related, no way to determine this
58                 -- Rule 5: Prefer outgoing interface
59                 -- XXX: Interface to address relation. No way to determine this
60                 -- Rule 6: Prefer matching label
61                 if ipA.label == dest.label and ipB.label ~= dest.label then
62                         return true;
63                 elseif ipB.label == dest.label and ipA.label ~= dest.label then
64                         return false;
65                 end
66
67                 -- Rule 7: Prefer temporary addresses (over public ones)
68                 -- XXX: No way to determine this
69                 -- Rule 8: Use longest matching prefix
70                 if commonPrefixLength(ipA, dest) > commonPrefixLength(ipB, dest) then
71                         return true;
72                 else
73                         return false;
74                 end
75         end
76
77         t_sort(candidates, comp);
78         return candidates[1];
79 end
80
81 local function destination(candidates, sources)
82         local sourceAddrs = {};
83         local function comp(ipA, ipB)
84                 local ipAsource = sourceAddrs[ipA];
85                 local ipBsource = sourceAddrs[ipB];
86                 -- Rule 1: Avoid unusable destinations
87                 -- XXX: No such information
88                 -- Rule 2: Prefer matching scope
89                 if ipA.scope == ipAsource.scope and ipB.scope ~= ipBsource.scope then
90                         return true;
91                 elseif ipA.scope ~= ipAsource.scope and ipB.scope == ipBsource.scope then
92                         return false;
93                 end
94
95                 -- Rule 3: Avoid deprecated addresses
96                 -- XXX: No way to determine this
97                 -- Rule 4: Prefer home addresses
98                 -- XXX: Mobility Address related, no way to determine this
99                 -- Rule 5: Prefer matching label
100                 if ipAsource.label == ipA.label and ipBsource.label ~= ipB.label then
101                         return true;
102                 elseif ipBsource.label == ipB.label and ipAsource.label ~= ipA.label then
103                         return false;
104                 end
105
106                 -- Rule 6: Prefer higher precedence
107                 if ipA.precedence > ipB.precedence then
108                         return true;
109                 elseif ipA.precedence < ipB.precedence then
110                         return false;
111                 end
112
113                 -- Rule 7: Prefer native transport
114                 -- XXX: No way to determine this
115                 -- Rule 8: Prefer smaller scope
116                 if ipA.scope < ipB.scope then
117                         return true;
118                 elseif ipA.scope > ipB.scope then
119                         return false;
120                 end
121
122                 -- Rule 9: Use longest matching prefix
123                 if commonPrefixLength(ipA, ipAsource) > commonPrefixLength(ipB, ipBsource) then
124                         return true;
125                 elseif commonPrefixLength(ipA, ipAsource) < commonPrefixLength(ipB, ipBsource) then
126                         return false;
127                 end
128
129                 -- Rule 10: Otherwise, leave order unchanged
130                 return true;
131         end
132         for _, ip in ipairs(candidates) do
133                 sourceAddrs[ip] = source(ip, sources);
134         end
135
136         t_sort(candidates, comp);
137         return candidates;
138 end
139
140 return {source = source,
141         destination = destination};