| 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. |