/** Find a ConfItem that has the same name and user+host fields as * specified. Requires an exact match for \a name. * @param name Name to match * @param cptr Client to match against * @param statmask Filter for ConfItem::status * @return First found matching ConfItem. */ struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int statmask) { struct ConfItem *tmp; for (tmp = GlobalConfList; tmp; tmp = tmp->next) { if (!(tmp->status & statmask) || !tmp->name || !tmp->host || 0 != ircd_strcmp(tmp->name, name)) continue; if (tmp->username && (EmptyString(cli_username(cptr)) || match(tmp->username, cli_username(cptr)))) continue; if (tmp->addrbits < 0) { if (match(tmp->host, cli_sockhost(cptr))) continue; } else if (!ipmask_check(&cli_ip(cptr), &tmp->address.addr, tmp->addrbits)) continue; if ((tmp->status & CONF_OPERATOR) && (MaxLinks(tmp->conn_class) > 0) && (tmp->clients >= MaxLinks(tmp->conn_class))) continue; return tmp; } return 0; }
/** Find Except configuration for \a cptr with flags matching \a flags * @param[in] cptr Client to match an Except configuration against. * @param[in] mask Bitmask of EFLAG_* flags to test for exemption. * @return -1 if an exception was found, 0 otherwise. */ int find_except_conf(struct Client *cptr, int flags) { struct ExceptConf *econf; if (flags & EFLAG_IPCHECK) { if (IsIPCheckExempt(cptr)) return -1; if (IsNotIPCheckExempt(cptr)) return 0; } for(econf = exceptConfList; econf; econf = econf->next) { if (!(econf->flags & flags)) continue; if (econf->usermask && match(econf->usermask, cli_username(cptr))) continue; if (econf->bits > 0) { if (!ipmask_check(&cli_ip(cptr), &econf->address, econf->bits)) continue; } else if (econf->hostmask && match(econf->hostmask, cli_sockhost(cptr))) continue; if (econf->flags & EFLAG_IPCHECK) SetIPCheckExempt(cptr); return -1; } if (flags & EFLAG_IPCHECK) SetNotIPCheckExempt(cptr); return 0; }
/** Find Spoofhost configuration for \a cptr with spoof host matching host and * a password matching \a passwd * @param[in] cptr Client to match Spoofhost configuration against. * @param[in] host Spoofhost host to look for, if NULL look for an autoapply Spoofhost. * @param[in] passwd Password to compare against Spoofhost configuration. * @param[out] status 0 for Success, 1 for invalid password and 2 for no Spoofhost configuration. * @return SHostConf struct of matching Spoofhost configuration or 0 on error. */ struct SHostConf* find_shost_conf(struct Client *cptr, char *host, char *passwd, int *status) { struct SHostConf* sconf; char *crypted; int res = 0; *status = 2; for(sconf = shostConfList; sconf; sconf = sconf->next) { if ((host == NULL) && !(sconf->flags & SHFLAG_AUTOAPPLY)) continue; if (host != NULL) { if (!(sconf->flags & SHFLAG_ISMASK) && strcmp(sconf->spoofhost, host)) continue; if ((sconf->flags & SHFLAG_ISMASK) && match(sconf->spoofhost, host)) continue; } if (sconf->usermask) { if (match(sconf->usermask, cli_username(cptr)) && !((sconf->flags & SHFLAG_MATCHUSER) && !match(sconf->usermask, cli_user(cptr)->username))) continue; } if (sconf->bits > 0) { if (!ipmask_check(&cli_ip(cptr), &sconf->address, sconf->bits)) continue; } else if (sconf->hostmask && match(sconf->hostmask, cli_sockhost(cptr))) continue; *status = 1; res = 0; if ((host == NULL) && (sconf->flags & SHFLAG_AUTOAPPLY)) { *status = 0; return sconf; } if (EmptyString(passwd) && !EmptyString(sconf->passwd)) continue; if (!EmptyString(passwd) && EmptyString(sconf->passwd)) continue; if (!EmptyString(passwd) && !EmptyString(sconf->passwd)) { crypted = ircd_crypt(passwd, sconf->passwd); if (!crypted) continue; res = strcmp(crypted, sconf->passwd); MyFree(crypted); } if (0 == res) { *status = 0; return sconf; } } return 0; }
/** Return the name of the client for various tracking and admin * purposes. The main purpose of this function is to return the * "socket host" name of the client, if that differs from the * advertised name (other than case). But, this can be used on any * client structure. * @param sptr Client to operate on. * @param showip If non-zero, append [username\@text-ip] to name. * @return Either cli_name(\a sptr) or a static buffer. */ const char* get_client_name(const struct Client* sptr, int showip) { static char nbuf[HOSTLEN * 2 + USERLEN + 5]; if (!MyConnect(sptr) || !showip) return cli_name(sptr); ircd_snprintf(0, nbuf, sizeof(nbuf), "%s[%s@%s]", cli_name(sptr), IsIdented(sptr) ? cli_username(sptr) : "", cli_sock_ip(sptr)); return nbuf; }
/* * release_auth_client - release auth client from auth system * this adds the client into the local client lists so it can be read by * the main io processing loop */ static void release_auth_client(struct Client* client) { assert(0 != client); cli_auth(client) = 0; cli_lasttime(client) = cli_since(client) = CurrentTime; if (cli_fd(client) > HighestFd) HighestFd = cli_fd(client); LocalClientArray[cli_fd(client)] = client; add_client_to_list(client); socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE); Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]", cli_username(client), cli_sockhost(client), cli_sock_ip(client))); }
/* * read_auth_reply - read the reply (if any) from the ident server * we connected to. * We only give it one shot, if the reply isn't good the first time * fail the authentication entirely. --Bleep */ void read_auth_reply(struct AuthRequest* auth) { char* username = 0; unsigned int len; /* * rfc1453 sez we MUST accept 512 bytes */ char buf[BUFSIZE + 1]; assert(0 != auth); assert(0 != auth->client); assert(auth == cli_auth(auth->client)); if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) { buf[len] = '\0'; Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf)); username = check_ident_reply(buf); Debug((DEBUG_LIST, "Username: %s", username)); } close(auth->fd); auth->fd = -1; Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket)); socket_del(&auth->socket); ClearAuth(auth); if (!EmptyString(username)) { ircd_strncpy(cli_username(auth->client), username, USERLEN); /* * Not needed, struct is zeroed by memset * auth->client->username[USERLEN] = '\0'; */ SetGotId(auth->client); ++ServerStats->is_asuc; if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FIN_ID); } else { ++ServerStats->is_abad; } unlink_auth_request(auth, &AuthPollList); if (IsDNSPending(auth)) link_auth_request(auth, &AuthIncompleteList); else { release_auth_client(auth->client); free_auth_request(auth); } }
/* * Create a new struct Client structure and set it to initial state. * * from == NULL, create local client (a client connected to a socket). * * from != NULL, create remote client (behind a socket associated with * the client defined by 'from'). * ('from' is a local client!!). */ struct Client* make_client(struct Client *from, int status) { struct Client* cptr = 0; struct Connection* con = 0; assert(!from || cli_verify(from)); cptr = alloc_client(); assert(0 != cptr); assert(!cli_magic(cptr)); assert(0 == from || 0 != cli_connect(from)); if (!from) { /* local client, allocate a struct Connection */ con = alloc_connection(); assert(0 != con); assert(!con_magic(con)); con_magic(con) = CONNECTION_MAGIC; con_fd(con) = -1; /* initialize struct Connection */ con_freeflag(con) = 0; con_nextnick(con) = CurrentTime - NICK_DELAY; con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1)); con_handler(con) = UNREGISTERED_HANDLER; con_client(con) = cptr; cli_local(cptr) = 1; /* Set certain fields of the struct Client */ cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime; cli_lastnick(cptr) = TStime(); } else con = cli_connect(from); /* use 'from's connection */ assert(0 != con); assert(con_verify(con)); cli_magic(cptr) = CLIENT_MAGIC; cli_connect(cptr) = con; /* set the connection and other fields */ cli_status(cptr) = status; cli_hnext(cptr) = cptr; strcpy(cli_username(cptr), "unknown"); return cptr; }
/** Get all the exception flags that apply to the client \a cptr * @param[in] cptr Client to find the exception flags for. * @return Bitmask of all the exception flags found to apply to \a cptr */ int get_except_flags(struct Client *cptr) { int flags = 0; struct ExceptConf *econf; for(econf = exceptConfList; econf; econf = econf->next) { if (econf->usermask && match(econf->usermask, cli_username(cptr))) continue; if (econf->bits > 0) { if (!ipmask_check(&cli_ip(cptr), &econf->address, econf->bits)) continue; } else if (econf->hostmask && match(econf->hostmask, cli_sockhost(cptr))) continue; flags |= econf->flags; } return flags; }
/** Find WebIRC configuration for \a cptr with password matching \a passwd * @param[in] cptr Client to match WebIRC configuration against. * @param[in] passwd Password to compare against WebIRC configuration. * @param[out] status 0 for Success, 1 for invalid password and 2 for no WebIRC configuration. * @return WebIRCConf struct of matching WebIRC configuration or 0 on error. */ struct WebIRCConf* find_webirc_conf(struct Client *cptr, char *passwd, int* status) { struct WebIRCConf *wconf; char *crypted; int res; *status = 2; if (!passwd) return 0; for(wconf = webircConfList; wconf; wconf = wconf->next) { if (wconf->usermask && match(wconf->usermask, cli_username(cptr))) continue; if (wconf->bits > 0) { if (!ipmask_check(&cli_ip(cptr), &wconf->address, wconf->bits)) continue; } else if (wconf->hostmask && match(wconf->hostmask, cli_sockhost(cptr))) continue; *status = 1; if (!wconf->passwd) { *status = 0; return wconf; } crypted = ircd_crypt(passwd, wconf->passwd); if (!crypted) continue; res = strcmp(crypted, wconf->passwd); MyFree(crypted); if (0 == res) { *status = 0; return wconf; } } return 0; }
/* * ms_svsident - server message handler * * parv[0] = sender prefix * parv[1] = Target numeric * parv[2] = New ident */ int ms_svsident(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; char *s; char *newident = NULL; int legalident=1; if (parc < 3) return need_more_params(sptr, "SVSIDENT"); /* Ignore SVSIDENT for a user that has quit */ if (!(acptr = findNUser(parv[1]))) return 0; if (IsChannelService(acptr)) return 0; newident = strdup(parv[2]); if (strlen(newident) > USERLEN) return protocol_violation(sptr, "Ident too long in SVSIDENT command"); for (s = newident; *s; s++) { if (!IsUserChar(*s)) { legalident = 0; break; } } if (legalident == 0) return protocol_violation(sptr, "Illegal characters in SVSIDENT ident"); ircd_strncpy(cli_user(acptr)->username, newident, USERLEN); ircd_strncpy(cli_username(acptr), newident, USERLEN); sendcmdto_serv_butone(sptr, CMD_SVSIDENT, cptr, "%s%s %s", acptr->cli_user->server->cli_yxx, acptr->cli_yxx, newident); return 0; }
/** Find the first (best) Client block to attach. * @param cptr Client for whom to check rules. * @return Authorization check result. */ enum AuthorizationCheckResult attach_iline(struct Client* cptr) { struct ConfItem* aconf; assert(0 != cptr); for (aconf = GlobalConfList; aconf; aconf = aconf->next) { if (aconf->status != CONF_CLIENT) continue; /* If you change any of this logic, please make corresponding * changes in conf_debug_iline() below. */ if (aconf->address.port && aconf->address.port != cli_listener(cptr)->addr.port) continue; if (aconf->username && match(aconf->username, cli_username(cptr))) continue; if (aconf->host && match(aconf->host, cli_sockhost(cptr))) continue; if (aconf->countrymask && match(aconf->countrymask, cli_countrycode(cptr))) continue; if (aconf->continentmask && match(aconf->continentmask, cli_continentcode(cptr))) continue; if ((aconf->addrbits >= 0) && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits)) continue; if (IPcheck_nr(cptr) > aconf->maximum) return ACR_TOO_MANY_FROM_IP; if (aconf->redirserver && !EmptyString(aconf->redirserver)) { send_reply(cptr, RPL_BOUNCE, aconf->redirserver, aconf->redirport); return ACR_NO_AUTHORIZATION; } if (aconf->username && !IsWebIRCUserIdent(cptr) && (aconf->flags & CONF_NOIDENTTILDE)) SetFlag(cptr, FLAG_DOID); return attach_conf(cptr, aconf); } return ACR_NO_AUTHORIZATION; }
/* * ms_nick - server message handler for nicks * parv[0] = sender prefix * parv[1] = nickname * * If from server, source is client: * parv[2] = timestamp * * Source is server: * parv[2] = hopcount * parv[3] = timestamp * parv[4] = username * parv[5] = hostname * parv[6] = umode (optional) * parv[parc-3] = IP# <- Only Protocol >= 10 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10 * parv[parc-1] = info * parv[0] = server */ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; char nick[NICKLEN + 2]; time_t lastnick = 0; int differ = 1; const char *type; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if ((IsServer(sptr) && parc < 8) || parc < 3) { sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C", parv[1], cptr); return need_more_params(sptr, "NICK"); } ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; if (IsServer(sptr)) { lastnick = atoi(parv[3]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(sptr)->lag = TStime() - lastnick; } else { lastnick = atoi(parv[2]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick; } /* * If do_nick_name() returns a null name OR if the server sent a nick * name and do_nick_name() changed it in some way (due to rules of nick * creation) then reject it. If from a server and we reject it, * and KILL it. -avalon 4/4/92 */ if (!do_nick_name(nick) || strcmp(nick, parv[1])) { send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]); ++ServerStats->is_kill; sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1], parv[0], cptr); sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])", IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1], nick, cli_name(cptr)); if (!IsServer(sptr)) { /* * bad nick _change_ */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)", parv[0], cli_name(&me), cli_name(cptr), parv[0], cli_user(sptr) ? cli_username(sptr) : "", cli_user(sptr) ? cli_name(cli_user(sptr)->server) : cli_name(cptr)); } return 0; } /* Check against nick name collisions. */ if ((acptr = FindClient(nick)) == NULL) /* No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); /* * If acptr == sptr, then we have a client doing a nick * change between *equivalent* nicknames as far as server * is concerned (user is changing the case of his/her * nickname or somesuch) */ if (acptr == sptr) { if (strcmp(cli_name(acptr), nick) != 0) /* Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); else /* Setting their nick to what it already is? Ignore it. */ return 0; } /* now we know we have a real collision. */ /* * Note: From this point forward it can be assumed that * acptr != sptr (point to different client structures). */ assert(acptr != sptr); /* * If the older one is "non-person", the new entry is just * allowed to overwrite it. Just silently drop non-person, * and proceed with the nick. This should take care of the * "dormant nick" way of generating collisions... */ if (IsUnknown(acptr) && MyConnect(acptr)) { ServerStats->is_ref++; IPcheck_connect_fail(acptr); exit_client(cptr, acptr, &me, "Overridden by other sign on"); return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Decide, we really have a nick collision and deal with it */ /* * NICK was coming from a server connection. * This means we have a race condition (two users signing on * at the same time), or two net fragments reconnecting with the same nick. * The latter can happen because two different users connected * or because one and the same user switched server during a net break. * If the TimeStamps are equal, we kill both (or only 'new' * if it was a ":server NICK new ..."). * Otherwise we kill the youngest when user@host differ, * or the oldest when they are the same. * We treat user and ~user as different, because if it wasn't * a faked ~user the AUTH wouldn't have added the '~'. * --Run * */ if (IsServer(sptr)) { struct irc_in_addr ip; /* * A new NICK being introduced by a neighbouring * server (e.g. message type ":server NICK new ..." received) * * compare IP address and username */ base64toip(parv[parc - 3], &ip); differ = (0 != memcmp(&cli_ip(acptr), &ip, sizeof(cli_ip(acptr)))) || (0 != ircd_strcmp(cli_user(acptr)->username, parv[4])); sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- " "%C %Tu (%s user@host))", acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick, differ ? "Different" : "Same"); } else { /* * A NICK change has collided (e.g. message type ":old NICK new"). * * compare IP address and username */ differ = (0 != memcmp(&cli_ip(acptr), &cli_ip(sptr), sizeof(cli_ip(acptr)))) || (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username)); sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to " "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick); } type = differ ? "overruled by older nick" : "nick collision from same user@host"; /* * Now remove (kill) the nick on our side if it is the youngest. * If no timestamp was received, we ignore the incoming nick * (and expect a KILL for our legit nick soon ): * When the timestamps are equal we kill both nicks. --Run * acptr->from != cptr should *always* be true (?). * * This exits the client sending the NICK message */ if ((differ && lastnick >= cli_lastnick(acptr)) || (!differ && lastnick <= cli_lastnick(acptr))) { ServerStats->is_kill++; if (!IsServer(sptr)) { /* If this was a nick change and not a nick introduction, we * need to ensure that we remove our record of the client, and * send a KILL to the whole network. */ assert(!MyConnect(sptr)); /* Inform the rest of the net... */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%C :%s (%s)", sptr, cli_name(&me), type); /* Don't go sending off a QUIT message... */ SetFlag(sptr, FLAG_KILLED); /* Remove them locally. */ exit_client_msg(cptr, sptr, &me, "Killed (%s (%s))", feature_str(FEAT_HIS_SERVERNAME), type); } else { /* If the origin is a server, this was a new client, so we only * send the KILL in the direction it came from. We have no * client record that we would have to clean up. */ sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)", parv[parc - 2], cli_name(&me), type); } /* If the timestamps differ and we just killed sptr, we don't need to kill * acptr as well. */ if (lastnick != cli_lastnick(acptr)) return 0; } /* Tell acptr why we are killing it. */ send_reply(acptr, ERR_NICKCOLLISION, nick); ServerStats->is_kill++; SetFlag(acptr, FLAG_KILLED); /* * This exits the client we had before getting the NICK message */ sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (%s)", acptr, feature_str(FEAT_HIS_SERVERNAME), type); exit_client_msg(cptr, acptr, &me, "Killed (%s (%s))", feature_str(FEAT_HIS_SERVERNAME), type); if (lastnick == cli_lastnick(acptr)) return 0; if (sptr == NULL) return 0; return set_nick_name(cptr, sptr, nick, parc, parv); }
/* * ms_nick - server message handler for nicks * parv[0] = sender prefix * parv[1] = nickname * * If from server, source is client: * parv[2] = timestamp * * Source is server: * parv[2] = hopcount * parv[3] = timestamp * parv[4] = username * parv[5] = hostname * parv[6] = umode (optional) * parv[parc-3] = IP# <- Only Protocol >= 10 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10 * parv[parc-1] = info * parv[0] = server */ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr; char nick[NICKLEN + 2]; time_t lastnick = 0; int differ = 1; int samelastnick = 0; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if ((IsServer(sptr) && parc < 8) || parc < 3) { sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C", parv[1], cptr); return need_more_params(sptr, "NICK"); } ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; if (IsServer(sptr)) { lastnick = atoi(parv[3]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(sptr)->lag = TStime() - lastnick; } else { lastnick = atoi(parv[2]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick; } /* * If do_nick_name() returns a null name OR if the server sent a nick * name and do_nick_name() changed it in some way (due to rules of nick * creation) then reject it. If from a server and we reject it, * and KILL it. -avalon 4/4/92 */ if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) { send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]); ++ServerStats->is_kill; sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1], parv[0], cptr); sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])", IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1], nick, cli_name(cptr)); if (!IsServer(sptr)) { /* * bad nick _change_ */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)", parv[0], cli_name(&me), cli_name(cptr), parv[0], cli_user(sptr) ? cli_username(sptr) : "", cli_user(sptr) ? cli_name(cli_user(sptr)->server) : cli_name(cptr)); } return 0; } /* * Check against nick name collisions. * * Put this 'if' here so that the nesting goes nicely on the screen :) * We check against server name list before determining if the nickname * is present in the nicklist (due to the way the below for loop is * constructed). -avalon */ assert(NULL == strchr(nick,'.')); acptr = FindClient(nick); if (!acptr) { /* * No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); } assert(0 != acptr); /* * If acptr == sptr, then we have a client doing a nick * change between *equivalent* nicknames as far as server * is concerned (user is changing the case of his/her * nickname or somesuch) */ if (acptr == sptr) { if (strcmp(cli_name(acptr), nick) == 0) /* * This is just ':old NICK old' type thing. * Just forget the whole thing here. There is * no point forwarding it to anywhere, * especially since servers prior to this * version would treat it as nick collision. */ return 0; /* NICK Message ignored */ else /* * Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Note: From this point forward it can be assumed that * acptr != sptr (point to different client structures). */ assert(acptr != sptr); /* * If the older one is "non-person", the new entry is just * allowed to overwrite it. Just silently drop non-person, * and proceed with the nick. This should take care of the * "dormant nick" way of generating collisions... */ if (IsUnknown(acptr) && MyConnect(acptr)) { ++ServerStats->is_ref; IPcheck_connect_fail(cli_ip(acptr)); exit_client(cptr, acptr, &me, "Overridden by other sign on"); return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Decide, we really have a nick collision and deal with it */ /* * NICK was coming from a server connection. * This means we have a race condition (two users signing on * at the same time), or two net fragments reconnecting with the same nick. * The latter can happen because two different users connected * or because one and the same user switched server during a net break. * If the TimeStamps are equal, we kill both (or only 'new' * if it was a ":server NICK new ..."). * Otherwise we kill the youngest when user@host differ, * or the oldest when they are the same. * We treat user and ~user as different, because if it wasn't * a faked ~user the AUTH wouldn't have added the '~'. * --Run * */ if (IsServer(sptr)) { /* * A new NICK being introduced by a neighbouring * server (e.g. message type ":server NICK new ..." received) * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) || (0 != ircd_strcmp(cli_user(acptr)->username, parv[4])); sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- " "%C %Tu (%s user@host))", acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick, differ ? "Different" : "Same"); } else { /* * A NICK change has collided (e.g. message type ":old NICK new"). * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) || (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username)); sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to " "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick); } /* * Now remove (kill) the nick on our side if it is the youngest. * If no timestamp was received, we ignore the incoming nick * (and expect a KILL for our legit nick soon ): * When the timestamps are equal we kill both nicks. --Run * acptr->from != cptr should *always* be true (?). * * This exits the client sending the NICK message */ if (cli_from(acptr) != cptr) { if ((differ && lastnick >= cli_lastnick(acptr)) || (!differ && lastnick <= cli_lastnick(acptr))) { if (!IsServer(sptr)) { ++ServerStats->is_kill; sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (Nick collision)", sptr, cli_name(&me)); assert(!MyConnect(sptr)); SetFlag(sptr, FLAG_KILLED); exit_client_msg(cptr, sptr, &me, "Killed (%s (Nick collision))", feature_str(FEAT_HIS_SERVERNAME)); sptr = 0; /* Make sure we don't use the dead client */ } else { /* We need to kill this incoming client, which hasn't been properly registered yet. * Send a KILL message upstream to the server it came from */ sendcmdto_one(&me, CMD_KILL, sptr, "%s :%s (Nick collision)", parv[parc-2], cli_name(&me)); } /* If the two have the same TS then we want to kill both sides, so * don't leave yet! */ if (lastnick != cli_lastnick(acptr)) return 0; /* Ignore the NICK */ } send_reply(acptr, ERR_NICKCOLLISION, nick); } ++ServerStats->is_kill; SetFlag(acptr, FLAG_KILLED); if (lastnick == cli_lastnick(acptr)) samelastnick = 1; /* * This exits the client we had before getting the NICK message */ if (differ) { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (older nick " "overruled)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (older " "nick overruled))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (older nick " "overruled))", feature_str(FEAT_HIS_SERVERNAME)); } else { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (nick collision from " "same user@host)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (nick " "collision from same user@host))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (nick collision from " "same user@host))", feature_str(FEAT_HIS_SERVERNAME)); } if (samelastnick) return 0; assert(0 != sptr); return set_nick_name(cptr, sptr, nick, parc, parv); }
/** * Exits a client of *any* type (user, server, etc) * from this server. Also, this generates all necessary prototol * messages that this exit may cause. * * This function implicitly exits all other clients depending on * this connection. * * For convenience, this function returns a suitable value for * m_function return value: * * CPTR_KILLED if (cptr == bcptr) * 0 if (cptr != bcptr) * * This function can be called in two ways: * 1) From before or in parse(), exiting the 'cptr', in which case it was * invoked as exit_client(cptr, cptr, &me,...), causing it to always * return CPTR_KILLED. * 2) Via parse from a m_function call, in which case it was invoked as * exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client * that generated the message in a way that we can assume he already * did remove acptr from memory himself (or in other cases we don't mind * because he will be delinked.) Or invoked as: * exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should * be removed. * In general: No generated SQUIT or QUIT should be sent to source link * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too). * * --Run * @param cptr Connection currently being handled by read_message. * @param victim Client being killed. * @param killer Client that made the decision to remove \a victim. * @param comment Reason for the exit. * @return CPTR_KILLED if cptr == bcptr, else 0. */ int exit_client(struct Client *cptr, struct Client* victim, struct Client* killer, const char* comment) { struct Client* acptr = 0; struct DLink *dlp; time_t on_for; char comment1[HOSTLEN + HOSTLEN + 2]; assert(killer); if (MyConnect(victim)) { SetFlag(victim, FLAG_CLOSING); if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim)) sendto_opmask_butone(0, SNO_CONNEXIT, "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>", cli_name(victim), cli_user(victim)->username, cli_user(victim)->host, comment, ircd_ntoa(&cli_ip(victim)), NumNick(victim) /* two %s's */); update_load(); on_for = CurrentTime - cli_firsttime(victim); if (IsUser(victim) || IsUserPort(victim)) auth_send_exit(victim); if (IsUser(victim)) log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s", cli_firsttime(victim), on_for, cli_user(victim)->username, cli_sockhost(victim), ircd_ntoa(&cli_ip(victim)), IsAccount(victim) ? cli_username(victim) : "0", NumNick(victim), /* two %s's */ cli_name(victim), cli_info(victim)); if (victim != cli_from(killer) /* The source knows already */ && IsClient(victim)) /* Not a Ping struct or Log file */ { if (IsServer(victim) || IsHandshake(victim)) sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment); else if (!IsConnecting(victim)) { if (!IsDead(victim)) { if (IsServer(victim)) sendcmdto_one(killer, CMD_ERROR, victim, ":Closing Link: %s by %s (%s)", cli_name(victim), cli_name(killer), comment); else sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)", cli_name(victim), cli_name(IsServer(killer) ? &his : killer), comment); } } if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) && (killer == &me || (IsServer(killer) && (strncmp(comment, "Leaf-only link", 14) || strncmp(comment, "Non-Hub link", 12))))) { /* * Note: check user == user needed to make sure we have the same * client */ if (cli_serv(victim)->user && *(cli_serv(victim))->by && (acptr = findNUser(cli_serv(victim)->by))) { if (cli_user(acptr) == cli_serv(victim)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s canceled: %s", acptr, cli_name(victim), comment); } else { /* * not right client, set by to empty string */ acptr = 0; *(cli_serv(victim))->by = '\0'; } } if (killer == &me) sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s", cli_name(victim), comment); } } /* * Close the Client connection first. */ close_connection(victim); } if (IsServer(victim)) { if (feature_bool(FEAT_HIS_NETSPLIT)) strcpy(comment1, "*.net *.split"); else { strcpy(comment1, cli_name(cli_serv(victim)->up)); strcat(comment1, " "); strcat(comment1, cli_name(victim)); } if (IsUser(killer)) sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:", (cli_user(killer)->server == victim || cli_user(killer)->server == cli_serv(victim)->up) ? "Local" : "Remote", get_client_name(killer, HIDE_IP), cli_name(cli_user(killer)->server)); else if (killer != &me && cli_serv(victim)->up != killer) sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :", cli_name(victim), IsServer(killer) ? cli_name(killer) : get_client_name(killer, HIDE_IP)); sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)", cli_serv(victim)->up, victim, comment); } /* * First generate the needed protocol for the other server links * except the source: */ for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) { if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim) { if (IsServer(victim)) sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s", cli_name(victim), cli_serv(victim)->timestamp, comment); else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED)) sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment); } } /* Then remove the client structures */ if (IsServer(victim)) exit_downlinks(victim, killer, comment1); exit_one_client(victim, comment); /* * cptr can only have been killed if it was cptr itself that got killed here, * because cptr can never have been a dependent of victim --Run */ return (cptr == victim) ? CPTR_KILLED : 0; }
int get_eflags(struct Client *sptr, struct Client *acptr) { struct eline *eline; unsigned int e_flag = 0; int found = 0; char outbuf[BUFSIZE]; char i_host[SOCKIPLEN + USERLEN + 2]; char s_host[HOSTLEN + USERLEN + 2]; ircd_snprintf(0, i_host, USERLEN+SOCKIPLEN+2, "%s@%s", cli_username(acptr), ircd_ntoa((const char*) &(cli_ip(acptr)))); ircd_snprintf(0, s_host, USERLEN+HOSTLEN+2, "%s@%s", cli_username(acptr), cli_sockhost(acptr)); for (eline = GlobalEList; eline; eline = eline->next) { char* ip_start; char* cidr_start; in_addr_t cli_addr = 0; *outbuf = '\0'; e_flag = eflagstr(eline->flags); if (s_host) { if ((match(eline->mask, s_host) == 0) || (match(eline->mask, i_host) == 0)) { found = 1; } } if ((ip_start = strrchr(i_host, '@'))) cli_addr = inet_addr(ip_start + 1); if ((ip_start = strrchr(eline->mask, '@')) && (cidr_start = strchr(ip_start + 1, '/'))) { int bits = atoi(cidr_start + 1); char* p = strchr(i_host, '@'); if (p) { *p = *ip_start = 0; if (match(eline->mask, i_host) == 0) { if ((bits > 0) && (bits < 33)) { in_addr_t ban_addr; *cidr_start = 0; ban_addr = inet_addr(ip_start + 1); *cidr_start = '/'; if ((NETMASK(bits) & cli_addr) == ban_addr) { *p = *ip_start = '@'; found = 1; } } } *p = *ip_start = '@'; } } if (found) { strcat(outbuf, " Exemptions:: "); if (e_flag & EFLAG_KLINE) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "K:Lines"); } if (e_flag & EFLAG_GLINE) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "G:Lines"); } if (e_flag & EFLAG_SHUN) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "Shuns"); } if (e_flag & EFLAG_ZLINE) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "Z:Lines"); } if (e_flag & EFLAG_SFILTER) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "Spamfilters"); } if (e_flag & EFLAG_LIST) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "List Delays"); } if (e_flag & EFLAG_IDENT) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "Ident Prompts"); } if ((e_flag & EFLAG_IPCHECK) && IsIPCheckExempted(acptr)) { if (strlen(outbuf) > 21) strcat(outbuf, ", "); strcat(outbuf, "IPCheck"); } if (strlen(outbuf) > 18) send_reply(sptr, RPL_DATASTR, outbuf); } found = 0; } return 0; }
/** Check access for a server given its name (passed in cptr struct). * Must check for all C/N lines which have a name which matches the * name given and a host which matches. A host alias which is the * same as the server name is also acceptable in the host field of a * C/N line. * @param cptr Peer server to check. * @return 0 if accepted, -1 if access denied. */ int conf_check_server(struct Client *cptr) { struct ConfItem* c_conf = NULL; struct SLink* lp; Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", cli_name(cptr), cli_sockhost(cptr))); if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) { Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr))); return -1; } lp = cli_confs(cptr); /* * We initiated this connection so the client should have a C and N * line already attached after passing through the connect_server() * function earlier. */ if (IsConnecting(cptr) || IsHandshake(cptr)) { c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER); if (!c_conf) { sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost Connect block for %s", cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } /* Try finding the Connect block by DNS name and IP next. */ if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER))) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * Attach by IP# only if all other checks have failed. * It is quite possible to get here with the strange things that can * happen when using DNS in the way the irc server does. -avalon */ if (!c_conf) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * detach all conf lines that got attached by attach_confs() */ det_confs_butmask(cptr, 0); /* * if no Connect block, then deny access */ if (!c_conf) { Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]", cli_name(cptr), cli_username(cptr), cli_sockhost(cptr))); return -1; } /* * attach the Connect block to the client structure for later use. */ attach_conf(cptr, c_conf); if (!irc_in_addr_valid(&c_conf->address.addr)) memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr)); Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cli_name(cptr), cli_sockhost(cptr))); return 0; }