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 }