1 |
$Id$ |
2 |
Protocol changes for +TSora |
3 |
--------------------------- |
4 |
|
5 |
Note: |
6 |
|
7 |
The protocols described here implement TimeStamps on IRC channels and |
8 |
nicks. The idea of IRC TimeStamps was started on Undernet, and first |
9 |
implemented by Run <carlo@runaway.xs4all.nl>. The protocols used here |
10 |
are not exactly the same as the ones used on Undernet; the nick-kill |
11 |
handling is very similar and must be credited to Run, while the |
12 |
"TimeStamped channel description" protocol is quite different. |
13 |
|
14 |
TSora servers keep track of which version of the TS protocol (if any) |
15 |
their neighboring servers are using, and take it into account when |
16 |
sending messages to them. This allows for seamless integration of TS |
17 |
servers into a non-TS net, and for upgrades of the protocol. |
18 |
|
19 |
Each server knows which is the lowest and the highest version of the |
20 |
TS protocol it can interact with; currently both of these are set to 1: |
21 |
|
22 |
#define TS_CURRENT 1 /* the highest TS ver we can do */ |
23 |
#define TS_MIN 1 /* the lowest TS ver we can do */ |
24 |
|
25 |
Timings and TS versions: |
26 |
======================== |
27 |
|
28 |
. Keep a 'delta' value to be added to the result of all calls to time(), |
29 |
initially 0. |
30 |
|
31 |
. Send a second argument to the PASS command, ending in the 'TS' string. |
32 |
|
33 |
. Send a |
34 |
|
35 |
SVINFO <TS_CURRENT> <TS_MIN> <STANDALONE> :<UTC-TIME> |
36 |
|
37 |
just after "SERVER", where <STANDALONE> is 1 if we're connected to |
38 |
more TSora servers, and 0 if not, and <UTC-TIME> is our idea of the |
39 |
current UTC time, fixed with the delta. |
40 |
|
41 |
. When we receive a "SVINFO <x> <y> <z> :<t>" line from a connecting |
42 |
server, we ignore it if TS_CURRENT<y or x<TS_MIN, otherwise we |
43 |
set a flag remembering that that server is TS-aware, remember the TS |
44 |
version to use with it (min(TS_CURRENT, x)). Additionally, if this is |
45 |
our first connected TS server, we set our delta to t-<OUR_UTC> if |
46 |
z==0, and to (t-<OUR_UTC>)/2 if z!=0. The SVINFO data is kept around |
47 |
until the server has effectively registered with SERVER, and used |
48 |
*after* sending our own SVINFO to that server. |
49 |
|
50 |
Explanations: |
51 |
|
52 |
Servers will always know which of their directly-linked servers can do |
53 |
TS, and will use the TS protocol only with servers that do understand |
54 |
it. This makes it possible to switch to full TS in just one |
55 |
code-replacement step, without incompatibilities. |
56 |
|
57 |
As long as not all servers are TS-aware, the net will be divided into |
58 |
"zones" of linked TS-aware servers. Channel modes will be kept |
59 |
synchronized at least within the zone in which the channel was |
60 |
created, and nick collisions between servers in the same zone will |
61 |
result in only one client being killed. |
62 |
|
63 |
Time synchronization ensures that servers have the same idea of the |
64 |
current time, and achieves this purpose as long as TS servers are |
65 |
introduced one by one within the same 'zone'. The merging of two zones |
66 |
cannot synchronize them completely, but it is to be expected that |
67 |
within each zone the effective time will be very close to the real |
68 |
time. |
69 |
|
70 |
By sending TSINFO after SERVER rather than before, we avoid the extra |
71 |
lag created by the identd check on the server. To be able to send |
72 |
immediately a connect burst of either type (TS or not), we need to |
73 |
know before that if the server does TS or not, so we send that |
74 |
information with PASS as an extra argument. And to avoid being |
75 |
incompatible with 2.9 servers, which check that this second argument |
76 |
begins with "2.9", we check that it *ends* with "TS". |
77 |
|
78 |
The current time is only used when setting a TS on a new channel or |
79 |
nick, and once such a TS is set, it is never modified because of |
80 |
synchronization, as it is much more important that the TS for a |
81 |
channel or nick stays the same across all servers than that it is |
82 |
accurate to the second. |
83 |
|
84 |
Note that Undernet's 2.8.x servers have no time synchronization at |
85 |
all, and have had no problems because of it - all of this is more to |
86 |
catch the occasional server with a way-off clock than anything. |
87 |
|
88 |
NICK handling patches (anti-nick-collide + shorter connect burst): |
89 |
================================================================== |
90 |
|
91 |
. For each nick, store a TS value = the TS value received if any, or our |
92 |
UTC+delta at the time we first heard of the nick. TS's are propagated |
93 |
to TS-aware servers whenever sending a NICK command. |
94 |
|
95 |
. Nick changes reset the TS to the current time. |
96 |
|
97 |
. When sending a connect burst to another TS server, replace the |
98 |
NICK/USER pair with only one NICK command containing the nick, the |
99 |
hopcount, the TS, the umode, and all the USER information. |
100 |
|
101 |
The format for a full NICK line is: |
102 |
NICK <nick> <hops> <TS> <umode> <user> <host> <server> :<ircname> |
103 |
|
104 |
The umode is a + followed by any applying usermodes. |
105 |
|
106 |
The format for a nick-change NICK line is: |
107 |
:<oldnick> NICK <newnick> :<TS> |
108 |
|
109 |
. When a NICK is received from a TS server, that conflicts with an |
110 |
existing nick: |
111 |
+ if the userhosts differ or one is not known: |
112 |
* if the timestamps are equal, kill ours and the old one if it |
113 |
was a nick change |
114 |
* if the incoming timestamp is older than ours, kill ours and |
115 |
propagate the new one |
116 |
* if the incoming timestamp is younger, ignore the line, but kill |
117 |
the old nick if it was a nick change |
118 |
+ if the userhosts are the same: |
119 |
* if the timestamps are equal, kill ours and the old one if it |
120 |
was a nick change |
121 |
* if the incoming timestamp is younger, kill ours and propagate |
122 |
the new one |
123 |
* if the incoming timestamp is older, ignore the line but kill |
124 |
the old nick if it was a nick change |
125 |
|
126 |
. When a NICK is received from a non-TS server that conflicts with |
127 |
an existing nick, kill both. |
128 |
|
129 |
. Do not send "Fake Prefix" kills in response to lines coming from TS |
130 |
servers; the sanitization works anyway, and this allows the "newer |
131 |
nick overruled" case to work. |
132 |
|
133 |
Explanations: |
134 |
|
135 |
The modified nick-introduction syntax allows for a slightly shorter |
136 |
connect-burst, and most importantly lets the server compare |
137 |
user@host's when determining which nick to kill: if the user@host |
138 |
is the same, then the older nick must be killed rather than the |
139 |
newer. |
140 |
|
141 |
When talking to a non-TS server, we need to behave exactly like one |
142 |
because it expects us to. When talkign to a TS server, we don't kill |
143 |
the nicks it's introducing, as we know it'll be smart enough to do it |
144 |
itself when seeing our own introduced nick. |
145 |
|
146 |
When we see a nick arriving from a non-TS server, it won't have a TS, |
147 |
but it's safe enough to give it the current time rather than keeping |
148 |
it 0; such TS's won't be the same all across the network (as long as |
149 |
there is more than one TS zone), and when there's a collision, the TS |
150 |
used will be the one in the zone the collision occurs in. |
151 |
|
152 |
Also, it is important to note that by the time a server sees (and |
153 |
chooses to ignore) a nick introduction, the introducing server has |
154 |
also had the time to put umode changes for that nick on its queue, so |
155 |
we must ignore them too... so we need to ignore fake-prefix lines |
156 |
rather than sending kills for them. This is safe enough, as the rest |
157 |
of the protocol ensures that they'll get killed anyway (and the |
158 |
Undernet does it too, so it's been more than enough tested). Just for |
159 |
an extra bit of compatibility, we still kill fake prefixes coming from |
160 |
non-TS servers. |
161 |
|
162 |
This part of the TS protocol is almost exactly the same as the |
163 |
Undernet's .anc (anti-nick-collide) patches, except that Undernet |
164 |
servers don't add usermodes to the NICK line. |
165 |
|
166 |
TimeStamped channel descriptions (avoiding hacked ops and desynchs): |
167 |
==================================================================== |
168 |
|
169 |
. For each channel, keep a timestamp, set to the current time when the |
170 |
channel is created by a client on the local server, or to the received |
171 |
value if the channel has been propagated from a TS server, or to 0 |
172 |
otherwise. This value will have the semantics of "the time of creation |
173 |
of the current ops on the channel", and 0 will mean that the channel |
174 |
is in non-TS mode. |
175 |
|
176 |
A new server protocol command is introduced, SJOIN, which introduces |
177 |
a full channel description: a timestamp, all the modes (except bans), |
178 |
and the list of channel members with their ops and voices. This |
179 |
command will be used instead of JOIN and of (most) MODEs both in |
180 |
connect bursts and when propagating channel creations among TS |
181 |
servers. SJOIN will never be accepted from or sent to users. |
182 |
|
183 |
The syntax for the command is: |
184 |
|
185 |
SJOIN <TS> #<channel> <modes> :[@][+]<nick_1> ... [@][+]<nick_n> |
186 |
|
187 |
The fields have the following meanings: |
188 |
|
189 |
* <TS> is the timestamp for the channel |
190 |
|
191 |
* <modes> is the list of global channel modes, starting with a + |
192 |
and a letter for each of the active modes (spmntkil), followed |
193 |
by an argument for +l if there is a limit, and an argument for |
194 |
+k if there's a key (in the same order they were mentioned in |
195 |
the string of letters). |
196 |
|
197 |
A channel with no modes will have a "+" in that field. |
198 |
|
199 |
A special value of "0" means that the server does not specify the |
200 |
modes, and will be used when more than one SJOIN line is needed |
201 |
to completely describe a channel, or when propagating a SJOIN |
202 |
the modes of which were rejected. |
203 |
|
204 |
* Each nick is preceded by a "@" if the user has ops, and a "+" if |
205 |
the user has a voice. For mode +ov, both flags are used. |
206 |
|
207 |
SJOINs will be propagated (when appropriate) to neighboring TS |
208 |
servers, and converted to JOINs and MODEs for neighboring non-TS |
209 |
servers. |
210 |
|
211 |
To propagate channels for which not all users fit in one |
212 |
SJOIN line, several SJOINs will be sent consecutively, only the first |
213 |
one including actual information in the <mode> field. |
214 |
|
215 |
An extra ad-hoc restriction is imposed on SJOIN messages, to simplify |
216 |
processing: if a channel has ops, then the first <nick> of the first |
217 |
SJOIN sent to propagate that channel must be one of the ops. |
218 |
|
219 |
Servers will never attempt to reconstruct a SJOIN from JOIN/MODE |
220 |
information being received at the moment from other servers. |
221 |
|
222 |
. For each user on a channel, keep an extra flag (like ops and voice) |
223 |
that is set when the user has received channel ops from another |
224 |
server (in a SJOIN channel description), which we rejected (ignored). |
225 |
Mode changes (but NOT kicks) coming from a TS server and from someone |
226 |
with this flag set will be ignored. The flag will be reset when the |
227 |
user gets ops from another user or server. |
228 |
|
229 |
. On deops done by non-local users, coming from TS servers, on channels |
230 |
with a non-zero TS, do not check that the user has ops but check that |
231 |
their 'deopped' flag is not set. For kicks coming from a TS server, do |
232 |
not check either. This will avoid desynchs, and 'bad' modechanges are |
233 |
avoided anyway. Other mode changes will still only be taken into |
234 |
account and propagated when done by users that are seen as having ops. |
235 |
|
236 |
. When a MODE change that ops someone is received from a server for a |
237 |
channel, that channel's TS is set to 0, and the mode change is |
238 |
propagated. |
239 |
|
240 |
. When a SJOIN is received for a channel, deal with it in this way: |
241 |
* received-TS = 0: |
242 |
+ if we have ops or the SJOIN doesn't op anyone, SJOIN propagated |
243 |
with our own TS. |
244 |
+ otherwise, TS set to 0 and SJOIN propagated with 0. |
245 |
* received-TS > 0, own-TS = 0: |
246 |
+ if the SJOIN ops someone or we don't have ops, set our TS to the |
247 |
received TS and propagate. |
248 |
+ otherwise, propagate with TS = 0. |
249 |
* received-TS = own-TS: propagate. |
250 |
* received-TS < own-TS: |
251 |
+ if the SJOIN ops someone, remove *all* modes (except bans) from |
252 |
the channel and propagate these mode changes to all neighboring |
253 |
non-TS servers, and copy the received TS and propagate the SJOIN. |
254 |
+ if the SJOIN does not op anyone and we have ops, propagate |
255 |
with our own TS. |
256 |
+ otherwise, copy the received TS and propagate the SJOIN. |
257 |
* received-TS > own-TS: |
258 |
+ if the SJOIN does not introduce any ops, process and propagate |
259 |
with our own TS. |
260 |
+ if we have ops: for each person the mode change would op, set the |
261 |
'deopped' flag; process all the JOINs ignoring the '@' and '+' |
262 |
flags; propagate without the flags and with our TS. |
263 |
+ if we don't have ops: set our TS to the received one, propagate |
264 |
with the flags. |
265 |
|
266 |
Explanations: |
267 |
|
268 |
This part of the protocol is the one that is most different (and |
269 |
incompatible) with the Undernet's: we never timestamp MODE changes, |
270 |
but instead we introduce the concept of time-stamped channel |
271 |
descriptions. This way each server can determine, based on its state |
272 |
and the received description, what the correct modes for a channel |
273 |
are, and deop its own users if necessary. With this protocol, there is |
274 |
*never* the need to reverse and bounce back a mode change. This is |
275 |
both faster and more bandwith-effective. |
276 |
|
277 |
The end goal is to have a protocol will eventually protect channels |
278 |
against hacked ops, while minimizing the impact on a mixed-server net. |
279 |
In order to do this, whenever there is a conflict between a TS server |
280 |
and a non-TS one, the non-TS one's idea of the whole situation |
281 |
prevails. This means that channels will only have a TS when they have |
282 |
been created on a TS-aware server, and will lose it whenever a server |
283 |
op comes from a non-TS server. Also, at most one 'zone' will have a TS |
284 |
for any given channel at any given time, ensuring that there won't be |
285 |
any deops when zones are merged. However, when TS zones are merged, if |
286 |
the side that has a TS also has ops, then the TS is kept across the |
287 |
whole new zone. Effective protection will only be ensured once all |
288 |
servers run TS patches and channels have been re-created, as there is |
289 |
no way servers can assign a TS to a channel they are not creating |
290 |
(like they do with nicks) without having unwanted deops later. |
291 |
|
292 |
The visible effects of this timestamped channel-description protocol |
293 |
are that when a split rejoins, and one side has hacked ops, the other |
294 |
side doesn't see any server mode changes (just like with Undernet's |
295 |
TS), but the side that has hacked ops sees: |
296 |
|
297 |
* first the first server on the other side deopping and devoicing |
298 |
everyone, and fixing the +spmntkli modes |
299 |
* then other users joining, and getting server ops and voices |
300 |
|
301 |
The less obvious part of this protocol is its behavior in the case |
302 |
that the younger side of a rejoin has servers that are lagged with |
303 |
each other. In such a situation, a SJOIN that clears all modes and |
304 |
sets the legitimate ones is being propagated from one server, and |
305 |
lagged illegitimate mode changes and kicks are being propagated in the |
306 |
opposite direction. In this case, a kick done by someone who is being |
307 |
deopped by the SJOIN must be taken into account to keep the name list |
308 |
in sync (and since it can only be kicking someone who also was on the |
309 |
younger side), while a deop does not matter (and will be ignored by |
310 |
the first server on the other side), and an opping *needs* to be |
311 |
discareded to avoid hacked ops. |
312 |
|
313 |
The main property of timestamped channel descriptions that makes them |
314 |
a very stable protocol even with lag and splits, is that they leave a |
315 |
server in the same final state, independently of the order in which |
316 |
channel descriptions coming from different servers are received. Even |
317 |
when SJOINs and MODEs for the same channel are being propagated in |
318 |
different direction because of several splits rejoining, the final |
319 |
state will be the same, independently of the exact order in which each |
320 |
server received the SJOINs, and will be the same across all the |
321 |
servers in the same zone. |