/** Start sending upings to a server. * @param[in] sptr Client requesting the upings. * @param[in] aconf ConfItem containing the address to ping. * @param[in] port Port number to ping. * @param[in] count Number of times to ping (should be at least 20). * @return Zero. */ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count) { int fd; struct UPing* pptr; assert(0 != sptr); assert(0 != aconf); if (INADDR_NONE == aconf->ipnum.s_addr) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for " "%s", sptr, aconf->name); return 0; } if (IsUPing(sptr)) uping_cancel(sptr, sptr); /* Cancel previous ping request */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Unable to create udp " "ping socket", sptr); return 0; } if (!os_set_nonblocking(fd)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't set fd non-" "blocking", sptr); close(fd); return 0; } pptr = (struct UPing*) MyMalloc(sizeof(struct UPing)); assert(0 != pptr); memset(pptr, 0, sizeof(struct UPing)); if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr, SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for " "reading", sptr); close(fd); MyFree(pptr); return 0; } pptr->fd = fd; pptr->sin.sin_port = htons(port); pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr; pptr->sin.sin_family = AF_INET; pptr->count = IRCD_MIN(20, count); pptr->client = sptr; pptr->index = -1; pptr->freeable = UPING_PENDING_SOCKET; strcpy(pptr->name, aconf->name); pptr->next = pingList; pingList = pptr; SetUPing(sptr); uping_start(pptr); return 0; }
/** Start sending upings to a server. * @param[in] sptr Client requesting the upings. * @param[in] aconf ConfItem containing the address to ping. * @param[in] port Port number to ping. * @param[in] count Number of times to ping (should be at least 20). * @return Zero. */ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count) { int fd; int family = 0; struct UPing* pptr; struct irc_sockaddr *local; assert(0 != sptr); assert(0 != aconf); if (!irc_in_addr_valid(&aconf->address.addr)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for " "%s", sptr, aconf->name); return 0; } if (IsUPing(sptr)) uping_cancel(sptr, sptr); /* Cancel previous ping request */ if (irc_in_addr_is_ipv4(&aconf->address.addr)) { local = &VirtualHost_v4; family = AF_INET; } else { local = &VirtualHost_v6; } fd = os_socket(local, SOCK_DGRAM, "Outbound uping socket", family); if (fd < 0) return 0; pptr = (struct UPing*) MyMalloc(sizeof(struct UPing)); assert(0 != pptr); memset(pptr, 0, sizeof(struct UPing)); if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr, SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for " "reading", sptr); close(fd); MyFree(pptr); return 0; } pptr->fd = fd; memcpy(&pptr->addr.addr, &aconf->address.addr, sizeof(pptr->addr.addr)); pptr->addr.port = port; pptr->count = IRCD_MIN(20, count); pptr->client = sptr; pptr->freeable = UPING_PENDING_SOCKET; strcpy(pptr->name, aconf->name); pptr->next = pingList; pingList = pptr; SetUPing(sptr); uping_start(pptr); return 0; }
/*! \brief NICK command handler (called by unregistered, * locally connected clients) * * \param client_p Pointer to allocated Client struct with physical connection * to this server, i.e. with an open socket connected. * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = sender prefix * - parv[1] = nickname */ static void mr_nick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char nick[NICKLEN + 1] = { '\0' }; char *s = NULL; struct Client *target_p = NULL; struct MaskItem *conf = NULL; if (parc < 2 || EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), me.name, source_p->name[0] ? source_p->name : "*"); return; } /* Terminate the nick at the first ~ */ if ((s = strchr(parv[1], '~')) != NULL) *s = '\0'; /* Copy the nick and terminate it */ strlcpy(nick, parv[1], IRCD_MIN(sizeof(nick), ServerInfo.max_nick_length + 1)); /* Check the nickname is ok */ if (!valid_nickname(nick, 1)) { sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name[0] ? source_p->name : "*", parv[1], "Erroneous Nickname"); return; } /* Check if the nick is resv'd */ if ((conf = find_matching_name_conf(CONF_NRESV, nick, NULL, NULL, 0))) { ++conf->count; sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name[0] ? source_p->name : "*", nick, conf->reason); sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE, "Forbidding reserved nick [%s] from user %s", nick, get_client_name(client_p, HIDE_IP)); return; } if ((target_p = hash_find_client(nick)) == NULL) set_initial_nick(source_p, nick); else if (source_p == target_p) strlcpy(source_p->name, nick, sizeof(source_p->name)); else sendto_one(source_p, form_str(ERR_NICKNAMEINUSE), me.name, "*", nick); }
/** Copy a single line from a data buffer. * If the output buffer cannot hold the whole line, or if there is no * EOL in the buffer, return 0. * @param[in,out] dyn Data buffer to copy from. * @param[out] buf Buffer to copy to. * @param[in] length Maximum number of bytes to copy. * @return Number of bytes copied to \a buf. */ unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length) { struct DBufBuffer *db; char *start; char *end; unsigned int count; unsigned int copied = 0; assert(0 != dyn); assert(0 != buf); if (0 == dbuf_flush(dyn)) return 0; assert(0 != dyn->head); db = dyn->head; start = db->start; assert(start < db->end); if (length > dyn->length) length = dyn->length; /* * might as well copy it while we're here */ while (length > 0) { end = IRCD_MIN(db->end, (start + length)); while (start < end && !IsEol(*start)) *buf++ = *start++; count = start - db->start; if (start < end) { *buf = '\0'; copied += count; dbuf_delete(dyn, copied); dbuf_flush(dyn); return copied; } if (0 == (db = db->next)) break; copied += count; length -= count; start = db->start; } return 0; }
/*! \brief NICK command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = nickname */ static int mr_nick(struct Client *source_p, int parc, char *parv[]) { char nick[NICKLEN + 1] = ""; struct Client *target_p = NULL; struct MaskItem *conf = NULL; if (parc < 2 || EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NONICKNAMEGIVEN); return 0; } /* Copy the nick and terminate it */ strlcpy(nick, parv[1], IRCD_MIN(sizeof(nick), ConfigServerInfo.max_nick_length + 1)); /* Check the nickname is ok */ if (!valid_nickname(nick, 1)) { sendto_one_numeric(source_p, &me, ERR_ERRONEUSNICKNAME, parv[1], "Erroneous Nickname"); return 0; } /* Check if the nick is resv'd */ if ((conf = find_matching_name_conf(CONF_NRESV, nick, NULL, NULL, 0))) { ++conf->count; sendto_one_numeric(source_p, &me, ERR_ERRONEUSNICKNAME, nick, conf->reason); sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE, "Forbidding reserved nick %s from user %s", nick, get_client_name(source_p, HIDE_IP)); return 0; } if ((target_p = hash_find_client(nick)) == NULL || target_p == source_p) set_initial_nick(source_p, nick); else sendto_one_numeric(source_p, &me, ERR_NICKNAMEINUSE, target_p->name); return 0; }
/*! \brief PASS command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = password * - parv[2] = unused * - parv[3] = TS protocol version * - parv[4] = server ID (SID) */ static int mr_pass(struct Client *source_p, int parc, char *parv[]) { assert(MyConnect(source_p)); if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "PASS"); return 0; } MyFree(source_p->connection->password); source_p->connection->password = xstrndup(parv[1], IRCD_MIN(strlen(parv[1]), PASSWDLEN)); /* Only do this stuff if we are doing ts6 */ if (parc > 4) if (atoi(parv[3]) >= 6 && valid_sid(parv[4])) strlcpy(source_p->id, parv[4], sizeof(source_p->id)); return 0; }
/** Send a message to all of our nameservers. * @param[in] msg Message to send. * @param[in] len Length of message. * @param[in] rcount Maximum number of servers to ask. * @return Number of servers that were successfully asked. */ static int send_res_msg(const char *msg, int len, int rcount) { int i; int sent = 0; int max_queries = IRCD_MIN(irc_nscount, rcount); /* RES_PRIMARY option is not implemented * if (res.options & RES_PRIMARY || 0 == max_queries) */ if (max_queries == 0) max_queries = 1; for (i = 0; i < max_queries; i++) { int fd = irc_in_addr_is_ipv4(&irc_nsaddr_list[i].addr) ? s_fd(&res_socket_v4) : s_fd(&res_socket_v6); if (os_sendto_nonb(fd, msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS) ++sent; } return(sent); }
/*! \brief PASS command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = password * - parv[2] = optional extra version information * - parv[3] = TS protocol version * - parv[4] = server ID (SID) */ static int mr_pass(struct Client *source_p, int parc, char *parv[]) { assert(MyConnect(source_p)); if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "PASS"); return 0; } MyFree(source_p->connection->password); source_p->connection->password = xstrndup(parv[1], IRCD_MIN(strlen(parv[1]), PASSWDLEN)); if (parc > 2) { /* * It looks to me as if orabidoo wanted to have more * than one set of option strings possible here... * i.e. ":AABBTS" as long as TS was the last two chars * however, as we are now using CAPAB, I think we can * safely assume if there is a ":TS" then it's a TS server * -Dianora */ if (!irccmp(parv[2], "TS") && source_p->tsinfo == 0) source_p->tsinfo = TS_DOESTS; } /* Only do this stuff if we are doing ts6 */ if (parc > 4) { if (atoi(parv[3]) >= 6 && valid_sid(parv[4])) { strlcpy(source_p->id, parv[4], sizeof(source_p->id)); SetCapable(source_p, CAP_TS6); } } return 0; }
/* * m_pass() - Added Sat, 4 March 1989 * * * mr_pass - PASS message handler * parv[0] = sender prefix * parv[1] = password * parv[2] = optional extra version information */ static void mr_pass(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { assert(client_p == source_p); if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name[0] ? source_p->name : "*", "PASS"); return; } MyFree(source_p->localClient->passwd); source_p->localClient->passwd = xstrndup(parv[1], IRCD_MIN(strlen(parv[1]), PASSWDLEN)); if (parc > 2) { /* It looks to me as if orabidoo wanted to have more * than one set of option strings possible here... * i.e. ":AABBTS" as long as TS was the last two chars * however, as we are now using CAPAB, I think we can * safely assume if there is a ":TS" then its a TS server * -Dianora */ if (!irccmp(parv[2], "TS") && source_p->tsinfo == 0) source_p->tsinfo = TS_DOESTS; } /* only do this stuff if we are doing ts6 */ if (parc > 4) { if (atoi(parv[3]) >= 6 && valid_sid(parv[4])) { strlcpy(source_p->id, parv[4], sizeof(source_p->id)); SetCapable(source_p, CAP_TS6); } } }
void fdlist_init(void) { int fdmax; struct rlimit limit; if (!getrlimit(RLIMIT_NOFILE, &limit)) { limit.rlim_cur = limit.rlim_max; setrlimit(RLIMIT_NOFILE, &limit); } fdmax = getdtablesize(); /* allow MAXCLIENTS_MIN clients even at the cost of MAX_BUFFER and * some not really LEAKED_FDS */ fdmax = IRCD_MAX(fdmax, LEAKED_FDS + MAX_BUFFER + MAXCLIENTS_MIN); /* under no condition shall this raise over 65536 * for example user ip heap is sized 2*hard_fdlimit */ hard_fdlimit = IRCD_MIN(fdmax, 65536); }
/* * ms_svsnick - server message handler * parv[0] = sender prefix * parv[1] = Target numeric * parv[2] = New nickname */ int ms_svsnick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr = NULL; struct Client* acptr2 = NULL; char nick[NICKLEN + 2]; char* arg; if (parc < 3) return need_more_params(sptr, "SVSNICK"); if (!(acptr = findNUser(parv[1]))) return 0; /* Ignore SVSNICK for a user that has quit */ if (ircd_strcmp(cli_name(acptr), parv[2]) == 0) return 0; /* Nick already set to what SVSNICK wants, ignoring... */ /* * Basic sanity checks */ /* * Don't let them make us send back a really long string of * garbage */ arg = parv[2]; if (strlen(arg) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))) arg[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))] = '\0'; strcpy(nick, arg); /* * If do_nick_name() returns a null name then reject it. */ if (0 == do_nick_name(nick)) return 0; /* * Check if this is a LOCAL user trying to use a reserved (Juped) * nick, if so tell him that it's a nick in use... */ if (isNickJuped(nick)) return 0; /* NICK message ignored */ if (feature_bool(FEAT_OPER_SINGLELETTERNICK) && !IsAnOper(acptr) && nick[1] == '\0') return 0; /* * Set acptr2 to the client pointer of any user with nick's name. * If the user is the same as the person being svsnick'ed, let it * through as it is probably a change in the nickname's case. */ if ((acptr2 = FindClient(nick))) { /* * If acptr == acptr2, 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), so we let it through :) */ if (acptr != acptr2) { /* Nick collision occured, kill user with specific reason */ ++ServerStats->is_kill; /* ??? - Why do we use cptr here? */ SetFlag(cptr, FLAG_KILLED); exit_client(cptr, acptr2, &me, "Killed (Nickname Enforcement)"); } } set_nick_name(acptr, acptr, nick, parc, parv, 1); sendcmdto_serv_butone(sptr, CMD_SVSNICK, cptr, "%s %s", parv[1], nick); return 0; }
/** Handle a JOIN message from a client connection. * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; struct JoinBuf join; struct JoinBuf create; struct Gline *gline; char *p = 0; char *chanlist; char *name; char *keys; if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */ keys = parv[2]; /* remember where keys are */ for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { char *key = 0; /* If we have any more keys, take the first for this channel. */ if (!BadPtr(keys) && (keys = strchr(key = keys, ','))) *keys++ = '\0'; /* Empty keys are the same as no keys. */ if (key && !key[0]) key = 0; if (!IsChannelName(name) || !strIsIrcCh(name)) { /* bad channel name */ send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (cli_user(sptr)->joined >= feature_int(FEAT_MAXCHANNELSPERUSER) && !HasPriv(sptr, PRIV_CHAN_LIMIT)) { send_reply(sptr, ERR_TOOMANYCHANNELS, name); break; /* no point processing the other channels */ } /* BADCHANed channel */ if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, name); continue; } if (!(chptr = FindChannel(name))) { if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(name) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (!(chptr = get_channel(sptr, name, CGT_CREATE))) continue; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); continue; } joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { continue; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { continue; } else { int flags = CHFL_DEOPPED; int err = 0; /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0]) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (chptr->mode.mode & MODE_INVITEONLY) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit)) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if (find_ban(sptr, chptr->banlist)) err = ERR_BANNEDFROMCHAN; else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key))) err = ERR_BADCHANNELKEY; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); continue; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } continue; } joinbuf_join(&join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ } joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ joinbuf_flush(&create); return 0; }
/*! \brief NICK command handler (called by already registered, * locally connected clients) * * \param client_p Pointer to allocated Client struct with physical connection * to this server, i.e. with an open socket connected. * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = sender prefix * - parv[1] = nickname */ static void m_nick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char nick[NICKLEN + 1] = { '\0' }; struct Client *target_p = NULL; struct MaskItem *conf = NULL; assert(source_p == client_p); if (parc < 2 || EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), me.name, source_p->name); return; } /* mark end of grace period, to prevent nickflooding */ if (!IsFloodDone(source_p)) flood_endgrace(source_p); /* terminate nick to NICKLEN */ strlcpy(nick, parv[1], IRCD_MIN(sizeof(nick), ServerInfo.max_nick_length + 1)); /* check the nickname is ok */ if (!valid_nickname(nick, 1)) { sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name, nick, "Erroneous Nickname"); return; } if (!IsExemptResv(source_p) && !(HasUMode(source_p, UMODE_OPER) && ConfigFileEntry.oper_pass_resv) && (conf = find_matching_name_conf(CONF_NRESV, nick, NULL, NULL, 0))) { ++conf->count; sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name, nick, conf->reason); sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE, "Forbidding reserved nick [%s] from user %s", nick, get_client_name(client_p, HIDE_IP)); return; } if ((target_p = hash_find_client(nick)) == NULL) change_local_nick(source_p, nick); else if (target_p == source_p) { /* * If (target_p == source_p) the client is changing nicks between * equivalent nicknames ie: [nick] -> {nick} */ /* Check the nick isn't exactly the same */ if (strcmp(target_p->name, nick)) change_local_nick(source_p, nick); } else if (IsUnknown(target_p)) { /* * If the client that has the nick isn't registered yet (nick but no * user) then drop the unregged client */ exit_client(target_p, &me, "Overridden"); change_local_nick(source_p, nick); } else sendto_one(source_p, form_str(ERR_NICKNAMEINUSE), me.name, source_p->name, nick); }
void do_join(struct Client *cptr, struct Client *sptr, struct JoinBuf *join, struct JoinBuf *create, char *chan, char *key, int level) { struct Channel *chptr; struct Gline *gline; /* BADCHANed channel */ if ((gline = gline_find(chan, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, chan); return; } /* Bouncy +L joins */ if (level > feature_int(FEAT_MAX_BOUNCE)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Couldn't join %s ! - Redirection (+L) setting was too bouncy", sptr, chan); return; } if (!(chptr = FindChannel(chan))) { if (((chan[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(chan) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (feature_bool(FEAT_CHANNEL_CREATE_IRCOPONLY) && !IsAnOper(sptr) && !IsChannelService(sptr)) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (!(chptr = get_channel(sptr, chan, CGT_CREATE))) return; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); return; } joinbuf_join(create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { return; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { return; } else { int flags = CHFL_DEOPPED; int err = 0; int excepted = 0; int exceptkli = 0; struct Ban *ban = NULL; if (*chptr->mode.redir && (*chptr->mode.redir != '\0')) { if (chptr->users >= chptr->mode.limit) { if (IsNoLink(sptr)) send_reply(sptr, ERR_LINKCHAN, chptr->chname, chptr->mode.redir); else if (!IsChannelName(chptr->mode.redir) || !strIsIrcCh(chptr->mode.redir)) send_reply(sptr, ERR_NOSUCHCHANNEL, chptr->mode.redir); else { send_reply(sptr, ERR_LINKSET, chptr->chname, chptr->chname, chptr->mode.redir); do_join(cptr, sptr, join, create, chptr->mode.redir, key, level+1); } return; } } if (find_ban(sptr, chptr->exceptlist, EBAN_EXCEPTLIST, 0)) { if (feature_bool(FEAT_CHMODE_e_CHMODEEXCEPTION)) exceptkli = 1; excepted = 1; } /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (feature_bool(FEAT_CHMODE_Z_STRICT) && (chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0] && !(chptr->mode.exmode & EXMODE_PERSIST)) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsXtraOp(sptr)) { /* XtraOp bypasses all other checks. */ } else if ((chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)) && !exceptkli) err = ERR_BADCHANNELKEY; else if (*chptr->mode.key && feature_bool(FEAT_FLEXIBLEKEYS) && (key && !strcmp(key, chptr->mode.key))) { /* Assume key checked by previous condition was found to be correct and allow join because FEAT_FLEXIBLEKEYS was enabled */ } else if ((chptr->mode.mode & MODE_INVITEONLY) && !exceptkli) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit) && !exceptkli) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if ((chptr->mode.exmode & EXMODE_ADMINONLY) && !IsAdmin(sptr)) err = ERR_ADMINONLYCHAN; else if ((chptr->mode.exmode & EXMODE_OPERONLY) && !IsAnOper(sptr)) err = ERR_OPERONLYCHAN; else if ((ban = find_ban(sptr, chptr->banlist, EBAN_NONE, 0)) && !excepted) err = ERR_BANNEDFROMCHAN; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); return; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; case ERR_ADMINONLYCHAN: err = 'a'; break; case ERR_OPERONLYCHAN: err = 'O'; break; case ERR_SSLONLYCHAN: err = 'Z'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } return; } joinbuf_join(join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ }
/*! \brief NICK command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = nickname */ static int m_nick(struct Client *source_p, int parc, char *parv[]) { char nick[NICKLEN + 1] = ""; struct Client *target_p = NULL; struct MaskItem *conf = NULL; assert(MyClient(source_p)); if (parc < 2 || EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NONICKNAMEGIVEN); return 0; } /* Mark end of grace period, to prevent nickflooding */ if (!IsFloodDone(source_p)) flood_endgrace(source_p); /* Terminate nick to NICKLEN */ strlcpy(nick, parv[1], IRCD_MIN(sizeof(nick), ConfigServerInfo.max_nick_length + 1)); /* Check the nickname is ok */ if (!valid_nickname(nick, 1)) { sendto_one_numeric(source_p, &me, ERR_ERRONEUSNICKNAME, nick, "Erroneous Nickname"); return 0; } if (!HasFlag(source_p, FLAGS_EXEMPTRESV) && !(HasUMode(source_p, UMODE_OPER) && HasOFlag(source_p, OPER_FLAG_NICK_RESV)) && (conf = find_matching_name_conf(CONF_NRESV, nick, NULL, NULL, 0))) { ++conf->count; sendto_one_numeric(source_p, &me, ERR_ERRONEUSNICKNAME, nick, conf->reason); sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE, "Forbidding reserved nick %s from user %s", nick, get_client_name(source_p, HIDE_IP)); return 0; } if ((target_p = hash_find_client(nick)) == NULL) change_local_nick(source_p, nick); else if (target_p == source_p) { /* * If (target_p == source_p) the client is changing nicks between * equivalent nicknames ie: [nick] -> {nick} */ /* Check the nick isn't exactly the same */ if (strcmp(target_p->name, nick)) change_local_nick(source_p, nick); } else if (IsUnknown(target_p)) { /* * If the client that has the nick isn't registered yet (nick but no * user) then drop the unregged client */ exit_client(target_p, "Overridden by other sign on"); change_local_nick(source_p, nick); } else sendto_one_numeric(source_p, &me, ERR_NICKNAMEINUSE, target_p->name); return 0; }
/* * m_nick - message handler for local clients * parv[0] = sender prefix * parv[1] = nickname */ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr; char nick[NICKLEN + 2]; char* arg; char* s; const char* client_name; assert(0 != cptr); assert(cptr == sptr); if (IsServerPort(cptr)) return exit_client(cptr, cptr, &me, "Use a different port"); /* * parv[0] will be empty for clients connecting for the first time */ client_name = (*(cli_name(sptr))) ? cli_name(sptr) : "*"; if (parc < 2) { send_reply(sptr, ERR_NONICKNAMEGIVEN); return 0; } /* * Don't let them send make us send back a really long string of * garbage */ arg = parv[1]; if (strlen(arg) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))) arg[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))] = '\0'; if ((s = strchr(arg, '~'))) *s = '\0'; strcpy(nick, arg); /* * If do_nick_name() returns a null name then reject it. */ if (0 == do_nick_name(nick)) { send_reply(sptr, ERR_ERRONEUSNICKNAME, arg); return 0; } /* * Check if this is a LOCAL user trying to use a reserved (Juped) * nick, if so tell him that it's a nick in use... */ if (isNickJuped(nick)) { send_reply(sptr, ERR_NICKNAMEINUSE, nick); return 0; /* NICK message ignored */ } if (!(acptr = FindClient(nick))) { /* * No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); } if (IsServer(acptr)) { send_reply(sptr, ERR_NICKNAMEINUSE, nick); return 0; /* NICK message ignored */ } /* * 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 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 (0 != strcmp(cli_name(acptr), nick)) { /* * Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); } /* * 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; } /* * 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... * * XXX - hmmm can this happen after one is registered? * * Yes, client 1 connects to IRC and registers, client 2 connects and * sends "NICK foo" but doesn't send anything more. client 1 now does * /nick foo, they should succeed and client 2 gets disconnected with * the message below. */ 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); } /* * NICK is coming from local client connection. Just * send error reply and ignore the command. */ send_reply(sptr, ERR_NICKNAMEINUSE, nick); return 0; /* NICK message ignored */ }
static time_t io_loop(time_t delay) { static char to_send[200]; static time_t lasttime = 0; static long lastrecvK = 0; static int lrv = 0; time_t lasttimeofday; lasttimeofday = CurrentTime; if (CurrentTime < lasttimeofday) { ircsprintf(to_send, "System clock is running backwards - (%d < %d)", CurrentTime, lasttimeofday); report_error(to_send, me.name, 0); } if(!next_gc) { next_gc = CurrentTime + 600; } /* * This chunk of code determines whether or not * "life sucks", that is to say if the traffic * level is so high that standard server * commands should be restricted * * Changed by Taner so that it tells you what's going on * as well as allows forced on (long LCF), etc... */ if ((CurrentTime - lasttime) >= LCF) { lrv = LRV * LCF; lasttime = CurrentTime; currlife = (float)((long)me.receiveK - lastrecvK)/(float)LCF; if (((long)me.receiveK - lrv) > lastrecvK ) { if (!LIFESUX) { LIFESUX = 1; if (NOISYHTM) { sprintf(to_send, "Entering high-traffic mode - (%.1fk/s > %dk/s)", (float)currlife, LRV); sendto_ops("%s", to_send); } } else { LIFESUX++; /* Ok, life really sucks! */ LCF += 2; /* Wait even longer */ if (NOISYHTM) { sprintf(to_send, "Still high-traffic mode %d%s (%d delay): %.1fk/s", LIFESUX, (LIFESUX & 0x04) ? " (TURBO)" : "", (int)LCF, (float)currlife); sendto_ops("%s", to_send); } } } else { LCF = LOADCFREQ; if (LIFESUX) { LIFESUX = 0; if (NOISYHTM) sendto_ops("Resuming standard operation . . . ."); } } lastrecvK = (long)me.receiveK; } /* ** We only want to connect if a connection is due, ** not every time through. Note, if there are no ** active C lines, this call to Tryconnections is ** made once only; it will return 0. - avalon */ if (nextconnect && CurrentTime >= nextconnect) nextconnect = try_connections(CurrentTime); /* ** take the smaller of the two 'timed' event times as ** the time of next event (stops us being late :) - avalon ** WARNING - nextconnect can return 0! */ if (nextconnect) delay = IRCD_MIN(nextping, nextconnect); delay -= CurrentTime; /* ** Adjust delay to something reasonable [ad hoc values] ** (one might think something more clever here... --msa) ** We don't really need to check that often and as long ** as we don't delay too long, everything should be ok. ** waiting too long can cause things to timeout... ** i.e. PINGS -> a disconnection :( ** - avalon */ if (delay < 1) delay = 1; else delay = IRCD_MIN(delay, TIMESEC); /* * We want to read servers on every io_loop, as well * as "busy" clients (which again, includes servers. * If "lifesux", then we read servers AGAIN, and then * flush any data to servers. * -Taner */ #ifndef NO_PRIORITY read_message(0, FDL_SERVER); read_message(0, FDL_BUSY); if (LIFESUX) { read_message(0, FDL_SERVER); if (LIFESUX & 0x4) { /* life really sucks */ read_message(0, FDL_BUSY); read_message(0, FDL_SERVER); } flush_server_connections(); } /* * CLIENT_SERVER = TRUE: * If we're in normal mode, or if "lifesux" and a few * seconds have passed, then read everything. * CLIENT_SERVER = FALSE: * If it's been more than lifesux*2 seconds (that is, * at most 1 second, or at least 2s when lifesux is * != 0) check everything. * -Taner */ { static time_t lslasttime=0; #ifdef CLIENT_SERVER if (!LIFESUX || (lslasttime + LIFESUX) < CurrentTime) { #else if ((lslasttime + (LIFESUX + 1)) < CurrentTime) { #endif read_message(0, FDL_ALL); /* check everything! */ lslasttime = CurrentTime; } } #else read_message(0, FDL_ALL); /* check everything! */ flush_server_connections(); #endif /* ** ...perhaps should not do these loops every time, ** but only if there is some chance of something ** happening (but, note that conf->hold times may ** be changed elsewhere--so precomputed next event ** time might be too far away... (similarly with ** ping times) --msa */ if (CurrentTime >= nextping) { nextping = check_pings(CurrentTime); timeout_auth_queries(CurrentTime); } if (dorehash && !LIFESUX) { rehash(&me, &me, 1); dorehash = 0; } /* ** Flush output buffers on all connections now if they ** have data in them (or at least try to flush) ** -avalon */ flush_connections(0); #ifndef NO_PRIORITY fdlist_check(CurrentTime); #endif if(CurrentTime >= next_gc) { block_garbage_collect(); next_gc = CurrentTime + 600; } return delay; } /* * initalialize_global_set_options * * inputs - none * output - none * side effects - This sets all global set options needed */ static void initialize_global_set_options(void) { memset( &GlobalSetOptions, 0, sizeof(GlobalSetOptions)); MAXCLIENTS = MAX_CLIENTS; NOISYHTM = NOISY_HTM; GlobalSetOptions.autoconn = 1; #ifdef FLUD FLUDNUM = FLUD_NUM; FLUDTIME = FLUD_TIME; FLUDBLOCK = FLUD_BLOCK; #endif #ifdef IDLE_CHECK IDLETIME = MIN_IDLETIME; #endif #ifdef ANTI_SPAMBOT SPAMTIME = MIN_JOIN_LEAVE_TIME; SPAMNUM = MAX_JOIN_LEAVE_COUNT; #endif #ifdef ANTI_DRONE_FLOOD DRONETIME = DEFAULT_DRONE_TIME; DRONECOUNT = DEFAULT_DRONE_COUNT; #endif #ifdef NEED_SPLITCODE SPLITDELAY = (DEFAULT_SERVER_SPLIT_RECOVERY_TIME * 60); SPLITNUM = SPLIT_SMALLNET_SIZE; SPLITUSERS = SPLIT_SMALLNET_USER_SIZE; server_split_time = CurrentTime; #endif /* End of global set options */ }