/************************************************************************ * IRC - Internet Relay Chat, modules/m_server.c * * Copyright (C) 2000-2003 TR-IRCD Development * * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Co Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * $Id: m_server.c,v 1.5 2003/06/14 13:55:51 tr-ircd Exp $ */ #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include "s_conf.h" #include "confitem.h" #include "hook.h" #include "h.h" static char *token = TOK1_SERVER; static int hookid_inform_remote_servers = 0; static struct Message _msgtab[] = { {MSG_SERVER, 0, MAXPARA, M_SLOW, 0L, m_server, m_ignore, m_ignore, s_server, m_ignore} }; #ifndef STATIC_MODULES char *_version = "$Revision: 1.5 $"; void _modinit(void) { mod_add_cmd(_msgtab); tok1_msgtab[(u_char) *token].msg = _msgtab; hookid_inform_remote_servers = hook_add_event("inform remote servers"); } void _moddeinit(void) { mod_del_cmd(_msgtab); tok1_msgtab[(u_char) *token].msg = NULL; } #else void m_server_init(void) { mod_add_cmd(_msgtab); tok1_msgtab[(u_char) *token].msg = _msgtab; hookid_inform_remote_servers = hook_add_event("inform remote servers"); } #endif /* * bogus_host * * inputs - hostname * output - 1 if a bogus hostname input, 0 if its valid * side effects - none */ static int bogus_host(char *host) { int bogus_server = 0; char *s; int dots = 0; for (s = host; *s; s++) { if (!IsServChar(*s)) { bogus_server = 1; break; } if ('.' == *s) ++dots; } if (!dots || bogus_server) return 1; return 0; } /* * parse_server_args * * inputs - parv parameters * - parc count * - info string (to be filled in by this routine) * - hop count (to be filled in by this routine) * output - NULL if invalid params, server name otherwise * side effects - parv[1] is trimmed to HOSTLEN size if needed. */ static char *parse_server_args(char *parv[], int parc, char *info, char *sid, int *flags, int *hop) { char *name; info[0] = '\0'; sid[0] = '\0'; if (parc < 2 || *parv[1] == '\0') return NULL; *hop = 0; *flags = 0; name = parv[1]; if (parc == 6) { *hop = atoi(parv[2]); *flags |= strchr(parv[3], 'H') ? PFLAGS_DOHIDENAME : 0; *flags |= strchr(parv[3], 'U') ? PFLAGS_ULINE : 0; *flags |= strchr(parv[3], 'R') ? PFLAGS_ISHUB : 0; strlcpy_irc(sid, parv[4] + 1, 8); sid[8] = '\0'; strlcpy_irc(info, parv[5], REALLEN); info[REALLEN] = '\0'; } else if ((parc == 5) && (parv[3][0] != '!')) { *hop = atoi(parv[2]); *flags |= strchr(parv[3], 'H') ? PFLAGS_DOHIDENAME : 0; *flags |= strchr(parv[3], 'U') ? PFLAGS_ULINE : 0; *flags |= strchr(parv[3], 'R') ? PFLAGS_ISHUB : 0; strlcpy_irc(info, parv[4], REALLEN); info[REALLEN] = '\0'; } else if ((parc == 5) && (parv[3][0] == '!')) { /* Kludge for Halycon */ *hop = atoi(parv[2]); strlcpy_irc(sid, parv[3] + 1, 8); sid[8] = '\0'; strlcpy_irc(info, parv[4], REALLEN); info[REALLEN] = '\0'; } else if (parc == 4) { *hop = atoi(parv[2]); *flags = 0; strlcpy_irc(info, parv[3], REALLEN); info[REALLEN] = '\0'; } else if (parc == 3) { *hop = 1; *flags = 0; strlcpy_irc(info, parv[2], REALLEN); info[REALLEN] = '\0'; } else if (parc == 2) { *hop = 1; *flags = 0; strlcpy_irc(info, "no description", REALLEN); info[REALLEN] = '\0'; } if (strlen(name) > HOSTLEN) name[HOSTLEN] = '\0'; return (name); } /* * server_exists() * * inputs - servername * output - 1 if server exists, 0 if doesnt exist */ static struct Client *server_exists(char *servername) { struct Client *target_p; dlink_node *ptr; for (ptr = global_serv_list.head; ptr; ptr = ptr->next) { target_p = ptr->data; if (!match(target_p->name, servername) || !match(servername, target_p->name)) return target_p; } return NULL; } /* * * m_server * parv[0] = sender prefix * parv[1] = servername * parv[2] = serverinfo */ int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]) { char info[REALLEN + 1]; char *host; char sid[8]; struct Client *target_p; int hop; int flags = 0; if ((host = parse_server_args(parv, parc, info, sid, &flags, &hop)) == NULL) { sendto_one_server(cptr, NULL, TOK1_ERROR, ":No servername"); return 0; } if (!DoesTS(cptr)) { sendto_gnotice("Link %s dropped, non-TS server", get_client_name(cptr, MASK_IP)); return exit_client(cptr, cptr, "Non-TS server"); } if (bogus_host(host)) { return exit_client(cptr, cptr, "Bogus server name"); } /* Now we just have to call check_server and everything should be * check for us... -A1kmm. */ switch (check_server(host, cptr)) { case -2: sendto_gnotice("Unauthorized server connection attempt from %s: No entry for " "servername %s", get_client_name(cptr, HIDE_IP), host); return exit_client(cptr, cptr, "Invalid servername."); break; case NOT_AUTHORIZED: sendto_gnotice("Unauthorized server connection attempt from %s: Bad password " "for server %s", get_client_name(cptr, HIDE_IP), host); return exit_client(cptr, cptr, "Invalid password."); break; case -3: sendto_gnotice("Unauthorized server connection attempt from %s: Invalid host " "for server %s", get_client_name(cptr, HIDE_IP), host); return exit_client(cptr, cptr, "Invalid host."); break; case INVALID_CONNECTION: sendto_gnotice("Unauthorized server connection attempt from %s to non-server port", get_client_name(cptr, HIDE_IP)); return exit_client(cptr, cptr, "No server port"); break; } if ((target_p = server_exists(host))) { /* * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon * * Definitely don't do that here. This is from an unregistered * connect - A1kmm. */ sendto_gnotice("Attempt to re-introduce server %s from %s", host, get_client_name(cptr, HIDE_IP)); sendto_one_server(cptr, NULL, TOK1_ERROR, ":Server already exists."); return exit_client(cptr, cptr, "Server Exists"); } /* * if we are connecting (Handshake), we already have the name from the * C:line in client_p->name */ strlcpy_irc(cptr->name, host, HOSTLEN); strlcpy_irc(cptr->info, info, REALLEN); cptr->hopcount = hop; cptr->protoflags |= flags; return server_estab(cptr); } /* * * m_server * parv[0] = sender prefix * parv[1] = servername * parv[2] = hopcount * parv[3] = hidechar * parv[4] = serverid * parv[5] = info */ int s_server(aClient *client_p, aClient *source_p, int parc, char *parv[]) { char info[REALLEN + 1]; char *name; struct Client *target_p; struct hook_data thisdata; int hop; /* for hopcount information */ char sid[8]; /* for server id */ int flags = 0; /* for hidechar information */ if ((name = parse_server_args(parv, parc, info, sid, &flags, &hop)) == NULL) { sendto_one_server(client_p, NULL, TOK1_ERROR, ":No servername"); return 0; } if ((target_p = server_exists(name))) { /* * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon * * I think that we should exit the link itself, not the introducer, * and we should always exit the most recently received(i.e. the * one we are receiving this SERVER for. -A1kmm * * You *cant* do this, if you link somewhere, it bursts you a server * that already exists, then sends you a client burst, you squit the * server, but you keep getting the burst of clients on a server that * doesnt exist, although ircd can handle it, its not a realistic * solution.. --fl_ */ /* It is behind a host-masked server. Completely ignore the * server message(don't propagate or we will delink from whoever * we propagate to). -A1kmm */ if (irc_strcmp(target_p->name, name) && target_p->from == client_p) return 0; if (client_p->firsttime > target_p->from->firsttime) { sendto_one_server(client_p, NULL, TOK1_ERROR, ":Server %s already exists", name); sendto_gnotice("Link %s cancelled, server %s already exists", get_client_name(client_p, SHOW_IP), name); return exit_client(client_p, &me, "Server Exists"); } else { sendto_one_server(target_p->from, NULL, TOK1_ERROR, ":Server %s already exists", name); sendto_gnotice("Link %s cancelled, server %s reintroduced by %s", get_client_name(target_p->from, SHOW_IP), name, client_p->name); return exit_client(target_p->from, &me, "Server Exists"); } } if (sid[0] && IsIDCapable(client_p)) { if (!valid_base64_server_id(sid)) { sendto_one_server(client_p, NULL, TOK1_ERROR, ":Invalid identity %s", sid); return 0; } } if (sid[0] && (target_p = find_server_by_base64_id(sid, NULL))) { /* * we have duplicate identities on the network. * this is most likely the result of some misconfiguration. * throw it out. */ sendto_one_server(client_p, NULL, TOK1_ERROR, ":Duplicate identity!"); sendto_gnotice("Link %s cancelled, identity %s already held by %s", get_client_name(client_p, HIDEME), sid, target_p->name); return exit_client(client_p, &me, "Duplicate identity"); } /* * User nicks never have '.' in them and server names * must always have '.' in them. */ if (strchr(name, '.') == NULL) { /* * Server trying to use the same name as a person. Would * cause a fair bit of confusion. Enough to make it hellish * for a while and servers to send stuff to the wrong place. */ sendto_one_server(client_p, NULL, TOK1_ERROR, ":Nickname %s already exists!", name); sendto_gnotice("Link %s cancelled: Server/nick collision on %s", /* inpath */ get_client_name(client_p, HIDE_IP), name); return exit_client(client_p, client_p, "Nick as Server"); } /* * Server is informing about a new server behind * this link. Create REMOTE server structure, * add it to list and propagate word to my other * server links... */ if (parc == 1 || info[0] == '\0') { sendto_one_server(client_p, NULL, TOK1_ERROR, ":No server info specified for %s", name); return 0; } /* * See if the newly found server is behind a guaranteed * leaf. If so, close the link. * Ok, check client_p can hub the new server, and make sure it's not a LL */ if (!IsHub(client_p)) { sendto_gnotice("Non-Hub link %s introduced %s.", get_client_name(client_p, HIDE_IP), name); /* If it is new, we are probably misconfigured, so split the * non-hub server introducing this. Otherwise, split the new * server. -A1kmm. */ if ((timeofday - source_p->firsttime) < 20) { return exit_client(source_p, &me, "No H-line."); } else { sendto_one_server(source_p, &me, TOK1_SQUIT, "%s :Sorry, no H-line.", name); return 0; } } /* * Since it is possible, that the remote server uses old protocol, * we might not know if they are U:Lined since the SERVER line * contains less parameters. On the other hand, in such cases, we * can check, if there is a corresponding U:Line or H:Line given * with the load directive in files { } section of the new ircd.conf * Then we can append the needed flags to the new structure. * -TimeMr14C */ target_p = make_client(client_p); make_server(target_p); target_p->hopcount = hop; target_p->protoflags |= flags; strlcpy_irc(target_p->name, name, HOSTLEN); strlcpy_irc(target_p->info, info, REALLEN); target_p->servptr = source_p; SetServer(target_p); Count.server++; GeneralOpts.split = 0; if (IsULine(source_p)) { target_p->protoflags |= PFLAGS_ULINE; sendto_gnotice("%s introducing U:lined server %s", client_p->name, target_p->name); } add_client_to_list(target_p); add_server_to_list(target_p); add_to_client_hash_table(target_p->name, target_p); add_client_to_llist(&(target_p->servptr->serv->servers), target_p); target_p->servptr->serv->servercnt++; if (sid) add_base64_server(target_p, sid); /* * Old sendto_serv_but_one() call removed because we now * need to send different names to different servers * (domain name matching) */ thisdata.client_p = client_p; thisdata.source_p = source_p; thisdata.aclient_p = target_p; thisdata.name = name; thisdata.check = hop; hook_call_event(hookid_inform_remote_servers, &thisdata); sendto_gnotice("Server %s being introduced by %s", target_p->name, source_p->name); return 0; }