unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
protoctl.c (12891B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/protoctl.c 3 * (C) 2004- The UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "unrealircd.h" 24 25 CMD_FUNC(cmd_protoctl); 26 27 #define MSG_PROTOCTL "PROTOCTL" 28 29 ModuleHeader MOD_HEADER 30 = { 31 "protoctl", 32 "5.0", 33 "command /protoctl", 34 "UnrealIRCd Team", 35 "unrealircd-6", 36 }; 37 38 MOD_INIT() 39 { 40 CommandAdd(modinfo->handle, MSG_PROTOCTL, cmd_protoctl, MAXPARA, CMD_UNREGISTERED|CMD_SERVER|CMD_USER); 41 MARK_AS_OFFICIAL_MODULE(modinfo); 42 return MOD_SUCCESS; 43 } 44 45 MOD_LOAD() 46 { 47 return MOD_SUCCESS; 48 } 49 50 MOD_UNLOAD() 51 { 52 return MOD_SUCCESS; 53 } 54 55 #define MAX_SERVER_TIME_OFFSET 60 56 57 /* The PROTOCTL command is used for negotiating capabilities with 58 * directly connected servers. 59 * See https://www.unrealircd.org/docs/Server_protocol:PROTOCTL_command 60 * for all technical documentation, especially if you are a server 61 * or services coder. 62 */ 63 CMD_FUNC(cmd_protoctl) 64 { 65 int i; 66 int first_protoctl = IsProtoctlReceived(client) ? 0 : 1; /**< First PROTOCTL we receive? Special ;) */ 67 char proto[512]; 68 char *name, *value, *p; 69 70 if (!MyConnect(client)) 71 return; /* Remote PROTOCTL's are not supported */ 72 73 SetProtoctlReceived(client); 74 75 for (i = 1; i < parc; i++) 76 { 77 strlcpy(proto, parv[i], sizeof proto); 78 p = strchr(proto, '='); 79 if (p) 80 { 81 name = proto; 82 *p++ = '\0'; 83 value = p; 84 } else { 85 name = proto; 86 value = NULL; 87 } 88 89 if (!strcmp(name, "NAMESX")) 90 { 91 SetCapability(client, "multi-prefix"); 92 } 93 else if (!strcmp(name, "UHNAMES") && UHNAMES_ENABLED) 94 { 95 SetCapability(client, "userhost-in-names"); 96 } 97 else if (IsUser(client)) 98 { 99 return; 100 } 101 else if (!strcmp(name, "VL")) 102 { 103 SetVL(client); 104 } 105 else if (!strcmp(name, "VHP")) 106 { 107 SetVHP(client); 108 } 109 else if (!strcmp(name, "CLK")) 110 { 111 SetCLK(client); 112 } 113 else if (!strcmp(name, "SJSBY") && iConf.ban_setter_sync) 114 { 115 SetSJSBY(client); 116 } 117 else if (!strcmp(name, "MTAGS")) 118 { 119 SetMTAGS(client); 120 } 121 else if (!strcmp(name, "NEXTBANS")) 122 { 123 SetNEXTBANS(client); 124 } 125 else if (!strcmp(name, "NICKCHARS") && value) 126 { 127 if (!IsServer(client) && !IsEAuth(client) && !IsHandshake(client)) 128 continue; 129 /* Ok, server is either authenticated, or is an outgoing connect... */ 130 /* Some combinations are fatal because they would lead to mass-kills: 131 * - use of 'utf8' on our server but not on theirs 132 */ 133 if (strstr(charsys_get_current_languages(), "utf8") && !strstr(value, "utf8")) 134 { 135 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CHARSYS_INCOMPATIBLE", client, 136 "Server link $client rejected. Server $me_name has utf8 in set::allowed-nickchars but $client does not.", 137 log_data_string("me_name", me.name)); 138 exit_client(client, NULL, "Incompatible set::allowed-nickchars setting"); 139 return; 140 } 141 /* We compare the character sets to see if we should warn opers about any mismatch... */ 142 if (strcmp(value, charsys_get_current_languages())) 143 { 144 unreal_log(ULOG_WARNING, "link", "LINK_WARNING_CHARSYS", client, 145 "Server link $client does not have the same set::allowed-nickchars settings, " 146 "this may possibly cause display issues. Our charset: '$our_charsys', theirs: '$their_charsys'", 147 log_data_string("our_charsys", charsys_get_current_languages()), 148 log_data_string("their_charsys", value)); 149 } 150 if (client->server) 151 safe_strdup(client->server->features.nickchars, value); 152 153 /* If this is a runtime change (so post-handshake): */ 154 if (IsServer(client)) 155 broadcast_sinfo(client, NULL, client); 156 } 157 else if (!strcmp(name, "CHANNELCHARS") && value) 158 { 159 int their_value; 160 161 if (!IsServer(client) && !IsEAuth(client) && !IsHandshake(client)) 162 continue; 163 164 their_value = allowed_channelchars_strtoval(value); 165 if (their_value != iConf.allowed_channelchars) 166 { 167 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_ALLOWED_CHANNELCHARS_INCOMPATIBLE", client, 168 "Server link $client rejected. Server has set::allowed-channelchars setting " 169 "of $their_allowed_channelchars, while we have $our_allowed_channelchars.\n" 170 "Please set set::allowed-channelchars to the same value on all servers.", 171 log_data_string("their_allowed_channelchars", value), 172 log_data_string("our_allowed_channelchars", allowed_channelchars_valtostr(iConf.allowed_channelchars))); 173 exit_client(client, NULL, "Incompatible set::allowed-channelchars setting"); 174 return; 175 } 176 } 177 else if (!strcmp(name, "SID") && value) 178 { 179 Client *aclient; 180 char *sid = value; 181 182 if (!IsServer(client) && !IsEAuth(client) && !IsHandshake(client)) 183 { 184 exit_client(client, NULL, "Got PROTOCTL SID before EAUTH, that's the wrong order!"); 185 return; 186 } 187 188 if (*client->id && (strlen(client->id)==3)) 189 { 190 exit_client(client, NULL, "Got PROTOCTL SID twice"); 191 return; 192 } 193 194 if (!valid_sid(value)) 195 { 196 exit_client(client, NULL, "Invalid SID. The first character must be a digit and the other two characters must be A-Z0-9. Eg: 0AA."); 197 return; 198 } 199 200 if (IsServer(client)) 201 { 202 exit_client(client, NULL, "Got PROTOCTL SID after SERVER, that's the wrong order!"); 203 return; 204 } 205 206 if ((aclient = hash_find_id(sid, NULL)) != NULL) 207 { 208 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SID_COLLISION", client, 209 "Server link $client rejected. Server with SID $sid already exist via uplink $existing_client.server.uplink.", 210 log_data_string("sid", sid), 211 log_data_client("existing_client", aclient)); 212 exit_client(client, NULL, "SID collision"); 213 return; 214 } 215 216 if (*client->id) 217 del_from_id_hash_table(client->id, client); /* delete old UID entry (created on connect) */ 218 strlcpy(client->id, sid, IDLEN); 219 add_to_id_hash_table(client->id, client); /* add SID */ 220 } 221 else if (!strcmp(name, "EAUTH") && value) 222 { 223 /* Early authorization: EAUTH=servername,protocol,flags,software 224 * (Only servername is mandatory, rest is optional) 225 */ 226 int ret; 227 char *p; 228 char *servername = NULL, *protocol = NULL, *flags = NULL, *software = NULL; 229 char buf[512]; 230 ConfigItem_link *aconf = NULL; 231 232 if (IsEAuth(client)) 233 { 234 exit_client(client, NULL, "PROTOCTL EAUTH received twice"); 235 return; 236 } 237 238 strlcpy(buf, value, sizeof(buf)); 239 p = strchr(buf, ' '); 240 if (p) 241 { 242 *p = '\0'; 243 p = NULL; 244 } 245 246 servername = strtoken_noskip(&p, buf, ","); 247 if (!servername || !valid_server_name(servername)) 248 { 249 exit_client(client, NULL, "Bogus server name"); 250 return; 251 } 252 253 254 protocol = strtoken_noskip(&p, NULL, ","); 255 if (protocol) 256 { 257 flags = strtoken_noskip(&p, NULL, ","); 258 if (flags) 259 software = strtoken_noskip(&p, NULL, ","); 260 } 261 262 /* Set client->name but don't add to hash list, this gives better 263 * log messages and should be safe. See CMTSRV941 in server.c. 264 */ 265 strlcpy(client->name, servername, sizeof(client->name)); 266 267 if (!(aconf = verify_link(client))) 268 return; 269 270 /* note: software, protocol and flags may be NULL */ 271 if (!check_deny_version(client, software, protocol ? atoi(protocol) : 0, flags)) 272 return; 273 274 SetEAuth(client); 275 make_server(client); /* allocate and set client->server */ 276 if (protocol) 277 client->server->features.protocol = atoi(protocol); 278 if (software) 279 safe_strdup(client->server->features.software, software); 280 if (is_services_but_not_ulined(client)) 281 { 282 exit_client_fmt(client, NULL, "Services detected but no ulines { } for server name %s", client->name); 283 return; 284 } 285 if (!IsHandshake(client) && aconf) /* Send PASS early... */ 286 sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); 287 } 288 else if (!strcmp(name, "SERVERS") && value) 289 { 290 Client *aclient, *srv; 291 char *sid = NULL; 292 293 if (!IsEAuth(client)) 294 continue; 295 296 if (client->server->features.protocol < 2351) 297 continue; /* old SERVERS= version */ 298 299 /* Other side lets us know which servers are behind it. 300 * SERVERS=<sid-of-server-1>[,<sid-of-server-2[,..etc..]] 301 * Eg: SERVERS=001,002,0AB,004,005 302 */ 303 304 add_pending_net(client, value); 305 306 aclient = find_non_pending_net_duplicates(client); 307 if (aclient) 308 { 309 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DUPLICATE_SID", client, 310 "Denied server $client: Server with SID $existing_client.id ($existing_client) is already linked.", 311 log_data_client("existing_client", aclient)); 312 exit_client(client, NULL, "Server Exists (or non-unique me::sid)"); 313 return; 314 } 315 316 aclient = find_pending_net_duplicates(client, &srv, &sid); 317 if (aclient) 318 { 319 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DUPLICATE_SID_LINKED", client, 320 "Denied server $client: Server would (later) introduce SID $sid, " 321 "but we already have SID $sid linked ($existing_client)\n" 322 "Possible race condition, just wait a moment for the network to synchronize...", 323 log_data_string("sid", sid), 324 log_data_client("existing_client", aclient)); 325 exit_client(client, NULL, "Server Exists (just wait a moment...)"); 326 return; 327 } 328 329 /* Send our PROTOCTL SERVERS= back if this was NOT a response */ 330 if (*value != '*') 331 send_protoctl_servers(client, 1); 332 } 333 else if (!strcmp(name, "TS") && value && (IsServer(client) || IsEAuth(client))) 334 { 335 long t = atol(value); 336 337 if (t < 10000) 338 continue; /* ignore */ 339 340 if ((TStime() - t) > MAX_SERVER_TIME_OFFSET) 341 { 342 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CLOCK_INCORRECT", client, 343 "Denied server $client: clock on server $client is $time_delta " 344 "seconds behind the clock of $me_name.\n" 345 "Correct time is very important for IRC servers, " 346 "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", 347 log_data_integer("time_delta", TStime() - t), 348 log_data_string("me_name", me.name)); 349 exit_client_fmt(client, NULL, "Incorrect clock. Our clocks are %lld seconds apart.", 350 (long long)(TStime() - t)); 351 return; 352 } else 353 if ((t - TStime()) > MAX_SERVER_TIME_OFFSET) 354 { 355 unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CLOCK_INCORRECT", client, 356 "Denied server $client: clock on server $client is $time_delta " 357 "seconds ahead the clock of $me_name.\n" 358 "Correct time is very important for IRC servers, " 359 "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", 360 log_data_integer("time_delta", t - TStime()), 361 log_data_string("me_name", me.name)); 362 exit_client_fmt(client, NULL, "Incorrect clock. Our clocks are %lld seconds apart.", 363 (long long)(t - TStime())); 364 return; 365 } 366 } 367 else if (!strcmp(name, "MLOCK")) 368 { 369 client->local->proto |= PROTO_MLOCK; 370 } 371 else if (!strcmp(name, "CHANMODES") && value && client->server) 372 { 373 parse_chanmodes_protoctl(client, value); 374 /* If this is a runtime change (so post-handshake): */ 375 if (IsServer(client)) 376 broadcast_sinfo(client, NULL, client); 377 } 378 else if (!strcmp(name, "USERMODES") && value && client->server) 379 { 380 safe_strdup(client->server->features.usermodes, value); 381 /* If this is a runtime change (so post-handshake): */ 382 if (IsServer(client)) 383 broadcast_sinfo(client, NULL, client); 384 } 385 else if (!strcmp(name, "BOOTED") && value && client->server) 386 { 387 client->server->boottime = atol(value); 388 } 389 else if (!strcmp(name, "EXTSWHOIS")) 390 { 391 client->local->proto |= PROTO_EXTSWHOIS; 392 } 393 /* You can add protocol extensions here. 394 * Use 'name' and 'value' (the latter may be NULL). 395 * 396 * DO NOT error or warn on unknown proto; we just don't 397 * support it. 398 */ 399 } 400 401 if (first_protoctl && IsHandshake(client) && client->server && !IsServerSent(client)) /* first & outgoing connection to server */ 402 { 403 /* SERVER message moved from completed_connection() to here due to EAUTH/SERVERS PROTOCTL stuff, 404 * which needed to be delayed until after both sides have received SERVERS=xx (..or not.. in case 405 * of older servers). 406 */ 407 send_server_message(client); 408 } 409 }