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