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