void init_list(void) { struct Client* cptr; struct Connection* con; int i; /* * pre-allocate MAXCONNECTIONS clients and connections */ for (i = 0; i < MAXCONNECTIONS; ++i) { cptr = (struct Client*) MyMalloc(sizeof(struct Client)); cli_next(cptr) = clientFreeList; clientFreeList = cptr; ++clientAllocCount; con = (struct Connection*) MyMalloc(sizeof(struct Connection)); con_next(con) = connectionFreeList; connectionFreeList = con; ++connectionAllocCount; } #ifdef DEBUGMODE memset(&clients, 0, sizeof(clients)); memset(&connections, 0, sizeof(connections)); memset(&users, 0, sizeof(users)); memset(&servs, 0, sizeof(servs)); memset(&links, 0, sizeof(links)); #endif }
/* * ms_links - server message handler * * parv[0] = sender prefix * parv[1] = servername mask * * or * * parv[0] = sender prefix * parv[1] = server to query * parv[2] = servername mask */ int ms_links(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; if (parc > 2) { if (hunt_server_cmd(sptr, CMD_LINKS, cptr, 1, "%C :%s", 1, parc, parv) != HUNTED_ISME) return 0; mask = parv[2]; } else mask = parc < 2 ? 0 : parv[1]; for (acptr = GlobalClientList, collapse(mask); acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (!BadPtr(mask) && match(mask, cli_name(acptr))) continue; send_reply(sptr, RPL_LINKS, cli_name(acptr), cli_name(cli_serv(acptr)->up), cli_hopcount(acptr), cli_serv(acptr)->prot, ((cli_info(acptr))[0] ? cli_info(acptr) : "(Unknown Location)")); } send_reply(sptr, RPL_ENDOFLINKS, BadPtr(mask) ? "*" : mask); return 0; }
/** Count number of users who match \a mask. * @param[in] mask ip mask to check. * @param[in] flags Bitmask possibly containing the value ZLINE_LOCAL, to limit searches to this server. * @return Count of matching users. */ static int count_users(char *mask, int flags) { struct irc_in_addr ipmask; struct Client *acptr; int count = 0; int ipmask_valid; char ipbuf[SOCKIPLEN + 2]; unsigned char ipmask_len; ipmask_valid = ipmask_parse(mask, &ipmask, &ipmask_len); for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsUser(acptr)) continue; if ((flags & ZLINE_LOCAL) && !MyConnect(acptr)) continue; ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s", ircd_ntoa(&cli_ip(acptr))); if (!match(mask, ipbuf) || (ipmask_valid && ipmask_check(&cli_ip(acptr), &ipmask, ipmask_len) && (irc_in_addr_type_cmp(&cli_ip(acptr), &ipmask) && ipmask_len))) count++; } return count; }
/** Link \a cptr into #GlobalClientList. * @param[in] cptr Client to link into the global list. */ void add_client_to_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); /* * Since we always insert new clients to the top of the list, * this should mean the "me" is the bottom most item in the list. * XXX - don't always count on the above, things change */ cli_prev(cptr) = 0; cli_next(cptr) = GlobalClientList; GlobalClientList = cptr; if (cli_next(cptr)) cli_prev(cli_next(cptr)) = cptr; }
/** Perform a very CPU-intensive verification of %GlobalClientList. * This checks the Client::cli_magic and Client::cli_prev field for * each element in the list, and also checks that there are no loops. * Any detected error will lead to an assertion failure. */ void verify_client_list(void) { struct Client *client, *prev = 0; unsigned int visited = 0; for (client = GlobalClientList; client; client = cli_next(client), ++visited) { /* Verify that this is a valid client, not a free'd one */ assert(cli_verify(client)); /* Verify that the list hasn't suddenly jumped around */ assert(cli_prev(client) == prev); /* Verify that the list hasn't become circular */ assert(cli_next(client) != GlobalClientList); assert(visited <= clients.alloc); /* Remember what should precede us */ prev = client; } }
/* WARNING: Major CPU sink! * * This is a debugging routine meant to verify the integrity of the client * linked list. It is meant to be comprehensive, to detect *any* corruption * of that list. This means that it will be majorly CPU-intensive, and * should *only* be enabled on servers that have DEBUGMODE enabled. Ignore * this warning at your peril! */ void verify_client_list(void) { struct Client *client, *prev = 0, *sentinel = 0; extern unsigned int ircrandom(void); for (client = GlobalClientList; client; client = cli_next(client)) { /* Verify that this is a valid client, not a free'd one */ assert(cli_verify(client)); /* Verify that the list hasn't suddenly jumped around */ assert(cli_prev(client) == prev); /* Verify that the list hasn't become circular */ assert(cli_next(client) != GlobalClientList); assert(!sentinel || client != sentinel); prev = client; /* Remember what should preceed us */ if (!(ircrandom() % 50)) /* probabilistic loop detector */ sentinel = client; } }
/** Release a Client structure by prepending it to #clientFreeList. * @param[in] cptr Client that is no longer being used. */ static void dealloc_client(struct Client* cptr) { assert(cli_verify(cptr)); assert(0 == cli_connect(cptr)); --clients.inuse; cli_next(cptr) = clientFreeList; clientFreeList = cptr; cli_magic(cptr) = 0; }
/** Release a Client. * In addition to the cleanup done by dealloc_client(), this will free * any pending auth request, free the connection for local clients, * and delete the processing timer for the client. * @param[in] cptr Client to free. */ void free_client(struct Client* cptr) { if (!cptr) return; /* * forget to remove the client from the hash table? */ assert(cli_verify(cptr)); assert(cli_hnext(cptr) == cptr); /* or from linked list? */ assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr), cptr, cli_connect(cptr))); if (cli_auth(cptr)) destroy_auth_request(cli_auth(cptr), 0); /* Make sure we didn't magically get re-added to the list */ assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); if (cli_from(cptr) == cptr) { /* in other words, we're local */ cli_from(cptr) = 0; /* timer must be marked as not active */ if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr)))) dealloc_connection(cli_connect(cptr)); /* connection not open anymore */ else { if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET) socket_del(&(cli_socket(cptr))); /* queue a socket delete */ if (cli_freeflag(cptr) & FREEFLAG_TIMER) timer_del(&(cli_proc(cptr))); /* queue a timer delete */ } } cli_connect(cptr) = 0; dealloc_client(cptr); /* actually destroy the client */ }
static void move_marker(void) { if (!++who_marker) { struct Client *cptr = GlobalClientList; while (cptr) { cli_marker(cptr) = 0; cptr = cli_next(cptr); } who_marker++; } }
/* * Taken the code from ExitOneClient() for this and placed it here. * - avalon */ void remove_client_from_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(con_verify(cli_connect(cptr))); assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr))); assert(!cli_next(cptr) || cli_verify(cli_next(cptr))); assert(!IsMe(cptr)); /* Only remove from the list if it actually IS in the list. * cli_next(cptr) cannot be NULL here if cptr is in the list, * only &me is at the end, and we never try to remove &me -GW */ if(cli_next(cptr)) { if (cli_prev(cptr)) cli_next(cli_prev(cptr)) = cli_next(cptr); else { GlobalClientList = cli_next(cptr); cli_prev(GlobalClientList) = 0; } cli_prev(cli_next(cptr)) = cli_prev(cptr); } cli_next(cptr) = cli_prev(cptr) = 0; if (IsUser(cptr) && cli_user(cptr)) { add_history(cptr, 0); off_history(cptr); } if (cli_user(cptr)) { free_user(cli_user(cptr)); cli_user(cptr) = 0; } if (cli_serv(cptr)) { if (cli_serv(cptr)->user) { free_user(cli_serv(cptr)->user); cli_serv(cptr)->user = 0; } if (cli_serv(cptr)->client_list) MyFree(cli_serv(cptr)->client_list); MyFree(cli_serv(cptr)->last_error_msg); MyFree(cli_serv(cptr)); #ifdef DEBUGMODE --servs.inuse; #endif } free_client(cptr); }
/** Remove \a cptr from lists that it is a member of. * Specifically, this delinks \a cptr from #GlobalClientList, updates * the whowas history list, frees its Client::cli_user and * Client::cli_serv fields, and finally calls free_client() on it. * @param[in] cptr Client to remove from lists and free. */ void remove_client_from_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(con_verify(cli_connect(cptr))); assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr))); assert(!cli_next(cptr) || cli_verify(cli_next(cptr))); assert(!IsMe(cptr)); /* Only try remove cptr from the list if it IS in the list. * cli_next(cptr) cannot be NULL here, as &me is always the end * the list, and we never remove &me. -GW */ if(cli_next(cptr)) { if (cli_prev(cptr)) cli_next(cli_prev(cptr)) = cli_next(cptr); else { GlobalClientList = cli_next(cptr); cli_prev(GlobalClientList) = 0; } cli_prev(cli_next(cptr)) = cli_prev(cptr); } cli_next(cptr) = cli_prev(cptr) = 0; if (IsUser(cptr) && cli_user(cptr)) { add_history(cptr, 0); off_history(cptr); } if (cli_user(cptr)) { free_user(cli_user(cptr)); cli_user(cptr) = 0; } if (cli_serv(cptr)) { if (cli_serv(cptr)->user) { free_user(cli_serv(cptr)->user); cli_serv(cptr)->user = 0; } if (cli_serv(cptr)->client_list) MyFree(cli_serv(cptr)->client_list); MyFree(cli_serv(cptr)->last_error_msg); MyFree(cli_serv(cptr)); --servs.inuse; --servs.alloc; } free_client(cptr); }
static int crule_connected(int numargs, void *crulearg[]) { struct Client *acptr; /* taken from m_links */ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (match((char *)crulearg[0], cli_name(acptr))) continue; return (1); } return (0); }
/** Allocate a new Client structure. * If #clientFreeList != NULL, use the head of that list. * Otherwise, allocate a new structure. * @return Newly allocated Client. */ static struct Client* alloc_client(void) { struct Client* cptr = clientFreeList; if (!cptr) { cptr = (struct Client*) MyMalloc(sizeof(struct Client)); clients.alloc++; } else clientFreeList = cli_next(cptr); clients.inuse++; memset(cptr, 0, sizeof(struct Client)); return cptr; }
/* * mo_squit (oper) * * parv[0] = sender prefix * parv[1] = server name * parv[2] = comment (optional) * */ int mo_squit(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char* server; struct Client *acptr; struct Client *acptr2; char *comment; if (parc < 2) return need_more_params(sptr, "SQUIT"); if (parc < 3 || BadPtr(parv[2])) comment = cli_name(sptr); else comment = parv[2]; server = parv[1]; /* * The following allows wild cards in SQUIT. Only useful * when the command is issued by an oper. */ for (acptr = GlobalClientList; (acptr = next_client(acptr, server)); acptr = cli_next(acptr)) { if (IsServer(acptr) || IsMe(acptr)) break; } /* Not found? Bugger. */ if (!acptr || IsMe(acptr)) return send_reply(sptr, ERR_NOSUCHSERVER, server); /* * Look for a matching server that is closer, * that way we won't accidentally squit two close * servers like davis.* and davis-r.* when typing * /SQUIT davis* */ for (acptr2 = cli_serv(acptr)->up; acptr2 != &me; acptr2 = cli_serv(acptr2)->up) if (!match(server, cli_name(acptr2))) acptr = acptr2; /* Disallow local opers to squit remote servers */ if (IsLocOp(sptr) && !MyConnect(acptr)) return send_reply(sptr, ERR_NOPRIVILEGES); return exit_client(cptr, acptr, sptr, comment); }
static int crule_via(int numargs, void *crulearg[]) { struct Client *acptr; /* adapted from m_links */ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (match((char *)crulearg[1], cli_name(acptr))) continue; if (match((char *)crulearg[0], cli_name(LocalClientArray[cli_fd(cli_from(acptr))]))) continue; return (1); } return (0); }
static struct Client* alloc_client(void) { struct Client* cptr = clientFreeList; if (!cptr) { cptr = (struct Client*) MyMalloc(sizeof(struct Client)); ++clientAllocCount; } else clientFreeList = cli_next(cptr); #ifdef DEBUGMODE clients.inuse++; #endif memset(cptr, 0, sizeof(struct Client)); return cptr; }
static void stats_servers_verbose(struct Client* sptr, struct StatDesc* sd, int stat, char* param) { struct Client *acptr; /* lowercase 'v' is for human-readable, * uppercase 'V' is for machine-readable */ if (stat == 'v') send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, "%-20s %-20s Flags Hops Numeric Lag RTT Up Down " "Clients/Max Proto %-10s :Info", "Servername", "Uplink", "LinkTS"); for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (param && match(param, cli_name(acptr))) /* narrow search */ continue; send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ? "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i " "P%-2i %Tu :%s" : "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s", cli_name(acptr), cli_name(cli_serv(acptr)->up), IsBurst(acptr) ? 'B' : '-', IsBurstAck(acptr) ? 'A' : '-', IsHub(acptr) ? 'H' : '-', IsService(acptr) ? 'S' : '-', cli_hopcount(acptr), NumServ(acptr), base64toint(cli_yxx(acptr)), cli_serv(acptr)->lag, cli_serv(acptr)->asll_rtt, cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from, cli_serv(acptr)->clients, cli_serv(acptr)->nn_mask, cli_serv(acptr)->prot, cli_serv(acptr)->timestamp, cli_info(acptr)); } }
/** Check whether a connected server matching crulearg[1] is * connnected to me behind one matching crulearg[0]. * @param[in] numargs Number of valid args in \a crulearg. * @param[in] crulearg Argument array. * @return Non-zero if the condition is true, zero if not. */ static int crule_via(int numargs, void *crulearg[]) { #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) struct Client *acptr; /* adapted from m_links */ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (match((char *)crulearg[1], cli_name(acptr))) continue; if (match((char *)crulearg[0], cli_name(cli_from(acptr)))) continue; return (1); } #endif return (0); }
/** Initialize the list manipulation support system. * Pre-allocate MAXCONNECTIONS Client and Connection structures. */ void init_list(void) { struct Client* cptr; struct Connection* con; int i; /* * pre-allocate MAXCONNECTIONS clients and connections */ for (i = 0; i < MAXCONNECTIONS; ++i) { cptr = (struct Client*) MyMalloc(sizeof(struct Client)); cli_next(cptr) = clientFreeList; clientFreeList = cptr; clients.alloc++; con = (struct Connection*) MyMalloc(sizeof(struct Connection)); con_next(con) = connectionFreeList; connectionFreeList = con; connections.alloc++; } }
/** Send a (prefixed) command to all users matching \a to as \a who. * @warning \a pattern must not contain %v. * @param[in] from Source of the command. * @param[in] cmd Long name of command. * @param[in] tok Short name of command. * @param[in] to Destination host/server mask. * @param[in] one Client direction to skip (or NULL). * @param[in] who Type of match for \a to (either MATCH_HOST or MATCH_SERVER). * @param[in] pattern Format string for command arguments. */ void sendcmdto_match_butone(struct Client *from, const char *cmd, const char *tok, const char *to, struct Client *one, unsigned int who, const char *pattern, ...) { struct VarData vd; struct Client *cptr; struct MsgBuf *user_mb; struct MsgBuf *serv_mb; vd.vd_format = pattern; /* Build buffer to send to users */ va_start(vd.vd_args, pattern); user_mb = msgq_make(0, "%:#C %s %v", from, cmd, &vd); va_end(vd.vd_args); /* Build buffer to send to servers */ va_start(vd.vd_args, pattern); serv_mb = msgq_make(&me, "%C %s %v", from, tok, &vd); va_end(vd.vd_args); /* send buffer along */ bump_sentalong(one); for (cptr = GlobalClientList; cptr; cptr = cli_next(cptr)) { if (!IsRegistered(cptr) || IsServer(cptr) || cli_fd(cli_from(cptr)) < 0 || cli_sentalong(cptr) == sentalong_marker || !match_it(from, cptr, to, who)) continue; /* skip it */ cli_sentalong(cptr) = sentalong_marker; if (MyConnect(cptr)) /* send right buffer */ send_buffer(cptr, user_mb, 0); else send_buffer(cptr, serv_mb, 0); } msgq_clean(user_mb); msgq_clean(serv_mb); }
/* * m_links - generic message handler * * parv[0] = sender prefix * parv[1] = servername mask * * or * * parv[0] = sender prefix * parv[1] = server to query * parv[2] = servername mask */ int m_links(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; if (feature_bool(FEAT_HIS_LINKS) && !IsAnOper(sptr)) { send_reply(sptr, RPL_ENDOFLINKS, parc < 2 ? "*" : parv[1]); sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s", sptr, "/LINKS has been disabled, from CFV-165. Visit ", feature_str(FEAT_HIS_URLSERVERS)); return 0; } if (parc > 2) { if (hunt_server_cmd(sptr, CMD_LINKS, cptr, 1, "%C :%s", 1, parc, parv) != HUNTED_ISME) return 0; mask = parv[2]; } else mask = parc < 2 ? 0 : parv[1]; for (acptr = GlobalClientList, collapse(mask); acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (!BadPtr(mask) && match(mask, cli_name(acptr))) continue; send_reply(sptr, RPL_LINKS, cli_name(acptr), cli_name(cli_serv(acptr)->up), cli_hopcount(acptr), cli_serv(acptr)->prot, ((cli_info(acptr))[0] ? cli_info(acptr) : "(Unknown Location)")); } send_reply(sptr, RPL_ENDOFLINKS, BadPtr(mask) ? "*" : mask); return 0; }
int count_users(char *mask) { struct Client *acptr; int count = 0; char namebuf[USERLEN + HOSTLEN + 2]; char ipbuf[USERLEN + 16 + 2]; for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsUser(acptr)) continue; ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s", cli_user(acptr)->username, cli_user(acptr)->realhost); ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username, ircd_ntoa((const char *) &(cli_ip(acptr)))); if (!match(mask, namebuf) || !match(mask, ipbuf)) count++; } return count; }
/* * Search and return as many people as matched by the wild 'nick'. * returns the number of people found (or, obviously, 0, if none where * found). */ static int do_wilds(struct Client* sptr, char *nick, int count, int parc) { struct Client *acptr; /* Current client we're considering */ struct User *user; /* the user portion of the client */ const char *name; /* the name of this client */ struct Membership* chan; int invis; /* does +i apply? */ int member; /* Is this user on any channels? */ int showperson; /* Should we show this person? */ int found = 0 ; /* How many were found? */ /* Ech! This is hideous! */ for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); acptr = cli_next(acptr)) { if (!IsRegistered(acptr)) continue; if (IsServer(acptr)) continue; /* * I'm always last :-) and acptr->next == 0!! * * Isomer: Does this strike anyone else as being a horrible hideous * hack? */ if (IsMe(acptr)) { assert(!cli_next(acptr)); break; } /* * 'Rules' established for sending a WHOIS reply: * * - if wildcards are being used don't send a reply if * the querier isn't any common channels and the * client in question is invisible. * * - only send replies about common or public channels * the target user(s) are on; */ user = cli_user(acptr); name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); assert(user); invis = (acptr != sptr) && IsInvisible(acptr); member = (user && user->channel) ? 1 : 0; showperson = !invis && !member; /* Should we show this person now? */ if (showperson) { found++; do_whois(sptr, acptr, parc); if (count+found>MAX_WHOIS_LINES) return found; continue; } /* Step through the channels this user is on */ for (chan = user->channel; chan; chan = chan->next_channel) { struct Channel *chptr = chan->channel; /* If this is a public channel, show the person */ if (!invis && PubChannel(chptr)) { showperson = 1; break; } /* if this channel is +p and not +s, show them */ if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) { showperson = 1; break; } member = find_channel_member(sptr, chptr) ? 1 : 0; if (invis && !member) continue; /* If sptr isn't really on this channel, skip it */ if (IsZombie(chan)) continue; /* Is this a common channel? */ if (member) { showperson = 1; break; } } /* of for (chan in channels) */ /* Don't show this person */ if (!showperson) continue; do_whois(sptr, acptr, parc); found++; if (count+found>MAX_WHOIS_LINES) return found; } /* of global client list */ return found; }
signed int checkHostmask(struct Client *sptr, char *hoststr, int flags) { struct Client *acptr; struct Channel *chptr; struct Membership *lp; int count = 0, found = 0, cidr_check_bits = 0; char outbuf[BUFSIZE]; char targhost[NICKLEN + USERLEN + HOSTLEN + 3], curhost[NICKLEN + USERLEN + HOSTLEN + 3]; char nickm[NICKLEN + 1], userm[USERLEN + 1], hostm[HOSTLEN + 1]; char *p = NULL; struct in_addr cidr_check; strcpy(nickm,"*"); strcpy(userm,"*"); strcpy(hostm,"*"); if (!strchr(hoststr, '!') && !strchr(hoststr, '@')) ircd_strncpy(hostm,hoststr,HOSTLEN); else { if ((p = strchr(hoststr, '@'))) { *p++ = '\0'; if (*p) ircd_strncpy(hostm,p, HOSTLEN); } /* Get the nick!user mask */ if ((p = strchr(hoststr, '!'))) { *p++ = '\0'; if (*p) ircd_strncpy(userm,p,USERLEN); if (*hoststr) ircd_strncpy(nickm,hoststr,NICKLEN); } else if (*hoststr) { /* Durz: We should only do the following *IF* the hoststr has not already been * copied into hostm (ie. neither ! or @ specified).. otherwise, when we do * /quote check *.barrysworld.com - we end up with targhost as: *!*.barryswo@*.barrysworld.com */ ircd_strncpy(userm,hoststr,USERLEN); } } if ((p = strchr(hostm, '/')) || inet_aton(hostm, &cidr_check)) { if (p) *p = '\0'; if (inet_aton(hostm, &cidr_check)) { cidr_check_bits = p ? atoi(p + 1) : 32; if ((cidr_check_bits >= 0) && (cidr_check_bits <= 32)) { flags |= CHECK_CIDRMASK; cidr_check.s_addr &= NETMASK(cidr_check_bits); } } if (p) *p = '/'; } /* Copy formatted string into "targhost" buffer */ ircd_snprintf(0, targhost, sizeof(targhost), "%s!%s@%s", nickm, userm, hostm); targhost[sizeof(targhost) - 1] = '\0'; /* Note: we have to exclude the last client struct as it is not a real client * structure, and therefore any attempt to access elements in it would cause * a segfault. */ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { /* Dont process if acptr is a unregistered client, a server or a ping */ if (!IsRegistered(acptr) || IsServer(acptr)) continue; if (IsMe(acptr)) /* Always the last acptr record */ break; if(count > feature_int(FEAT_MAX_CHECK_OUTPUT)) { /* sanity stuff */ ircd_snprintf(0, outbuf, sizeof(outbuf), "More than %d results, truncating...", count); send_reply(sptr, RPL_DATASTR, outbuf); send_reply(sptr, RPL_ENDOFCHECK, " "); break; } /* Copy host info into buffer */ curhost[0] = '\0'; ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, acptr->cli_user->realusername, acptr->cli_user->realhost); if (flags & CHECK_CIDRMASK) { if (((cli_ip(acptr).s_addr & NETMASK(cidr_check_bits)) == cidr_check.s_addr) && !match(nickm, acptr->cli_name) && (!match(userm, acptr->cli_user->realusername) || !match(userm, acptr->cli_user->username))) found = 1; } else { if(match((const char*)targhost,(const char*)curhost) == 0) found = 1; else { curhost[0] = '\0'; ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, acptr->cli_user->username, acptr->cli_user->host); if(match((const char*)targhost,(const char*)curhost) == 0) found = 1; } } if (found == 1) { found = 0; /* reset that so it doesn't get crazy go nuts */ /* Show header if we've found at least 1 record */ if (count == 0) { /* Output header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "host", targhost); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), "%s %-*s%-*s%s", "No.", (NICKLEN + 2 ), "Nick", (USERLEN + 2), "User", "Host"); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d %-*s%-*s%s", (count+1), (NICKLEN + 2), acptr->cli_name, (USERLEN + 2), acptr->cli_user->realusername, (flags & CHECK_SHOWIPS) ? ircd_ntoa((const char*)&(cli_ip(acptr))) : acptr->cli_user->realhost); send_reply(sptr, RPL_DATASTR, outbuf); /* Show channel output (if applicable) - the 50 channel limit sanity check * is specifically to prevent coredumping when someone lamely tries to /check * Q or some other channel service... */ if (flags & CHECK_CHECKCHAN) { if (acptr->cli_user->joined > 0 && acptr->cli_user->joined <= 50) { char chntext[BUFSIZE]; int len = strlen(" on channels: "); int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name); *chntext = '\0'; strcpy(chntext, " on channels: "); for (lp = acptr->cli_user->channel; lp; lp = lp->next_channel) { chptr = lp->channel; if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, RPL_DATASTR, chntext); *chntext = '\0'; strcpy(chntext, " on channels: "); len = strlen(chntext); } if (IsDeaf(acptr)) *(chntext + len++) = '-'; if (is_chan_op(acptr, chptr)) *(chntext + len++) = '@'; if (is_half_op(acptr, chptr)) *(chntext + len++) = '%'; if (IsOper(sptr) && !ShowChannel(sptr,chptr)) *(chntext + len++) = '*'; else if (has_voice(acptr, chptr)) *(chntext + len++) = '+'; else if (IsZombie(lp)) *(chntext + len++) = '!'; if (len) *(chntext + len) = '\0'; strcpy(chntext + len, chptr->chname); len += strlen(chptr->chname); strcat(chntext + len, " "); len++; } if (chntext[0] != '\0') send_reply(sptr, RPL_DATASTR, chntext); send_reply(sptr, RPL_DATASTR, " "); } } count++; } } if (count > 0) { send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), "Matching records found:: %d", count); send_reply(sptr, RPL_DATASTR, outbuf); send_reply(sptr, RPL_ENDOFCHECK, " "); } return count; }
/* Rewritten by Run - 24 sept 94 */ static void exit_one_client(struct Client* bcptr, const char* comment) { struct SLink *lp; struct Ban *bp; if (cli_serv(bcptr) && cli_serv(bcptr)->client_list) /* Was SetServerYXX called ? */ ClearServerYXX(bcptr); /* Removes server from server_list[] */ if (IsUser(bcptr)) { /* * clear out uping requests */ if (IsUPing(bcptr)) uping_cancel(bcptr, 0); /* * Stop a running /LIST clean */ if (MyUser(bcptr) && cli_listing(bcptr)) { MyFree(cli_listing(bcptr)); cli_listing(bcptr) = NULL; } /* * If a person is on a channel, send a QUIT notice * to every client (person) on the same channel (so * that the client can show the "**signoff" message). * (Note: The notice is to the local clients *only*) */ sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment); remove_user_from_all_channels(bcptr); /* Clean up invitefield */ while ((lp = cli_user(bcptr)->invited)) del_invite(bcptr, lp->value.chptr); /* Clean up silencefield */ while ((bp = cli_user(bcptr)->silence)) { cli_user(bcptr)->silence = bp->next; free_ban(bp); } /* Clean up snotice lists */ if (MyUser(bcptr)) set_snomask(bcptr, ~0, SNO_DEL); if (IsInvisible(bcptr)) { assert(UserStats.inv_clients > 0); --UserStats.inv_clients; } if (IsOper(bcptr)) { assert(UserStats.opers > 0); --UserStats.opers; } if (MyConnect(bcptr)) Count_clientdisconnects(bcptr, UserStats); else Count_remoteclientquits(UserStats, bcptr); } else if (IsServer(bcptr)) { /* Remove downlink list node of uplink */ remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown); cli_serv(bcptr)->updown = 0; if (MyConnect(bcptr)) Count_serverdisconnects(UserStats); else Count_remoteserverquits(UserStats); } else if (IsMe(bcptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self! */ } else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr)) Count_unknowndisconnects(UserStats); /* * Update IPregistry */ if (IsIPChecked(bcptr)) IPcheck_disconnect(bcptr); /* * Remove from serv->client_list * NOTE: user is *always* NULL if this is a server */ if (cli_user(bcptr)) { assert(!IsServer(bcptr)); /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */ RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr)); } /* Remove bcptr from the client list */ #ifdef DEBUGMODE if (hRemClient(bcptr) != 0) Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p", bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host", cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr), cli_status(bcptr), cli_user(bcptr))); #else hRemClient(bcptr); #endif remove_client_from_list(bcptr); }
/** Report memory usage statistics to a client. * @param cptr Client to send data to. * @param sd StatDesc that generated the stats request (ignored). * @param param Extra parameter from user (ignored). */ void count_memory(struct Client *cptr, const struct StatDesc *sd, char *param) { struct Client *acptr; struct Invite *inv; struct SLink *link; struct Ban *ban; struct Channel *chptr; struct ConfItem *aconf; const struct ConnectionClass* cltmp; struct Membership* member; int acc = 0, /* accounts */ c = 0, /* clients */ cn = 0, /* connections */ ch = 0, /* channels */ lcc = 0, /* local client conf links */ chb = 0, /* channel bans */ wwu = 0, /* whowas users */ cl = 0, /* classes */ co = 0, /* conf lines */ listeners = 0, /* listeners */ memberships = 0; /* channel memberships */ int usi = 0, /* users invited */ aw = 0, /* aways set */ wwa = 0, /* whowas aways */ gl = 0, /* glines */ ju = 0; /* jupes */ size_t chm = 0, /* memory used by channels */ chbm = 0, /* memory used by channel bans */ cm = 0, /* memory used by clients */ cnm = 0, /* memory used by connections */ us = 0, /* user structs */ usm = 0, /* memory used by user structs */ awm = 0, /* memory used by aways */ wwam = 0, /* whowas away memory used */ wwm = 0, /* whowas array memory used */ wt = 0, /* watch entrys */ wtm = 0, /* memory used by watchs */ #if defined(DDB) dbs = 0, /* keys of database */ dbm = 0, /* memory used by DDB */ #endif glm = 0, /* memory used by glines */ jum = 0, /* memory used by jupes */ com = 0, /* memory used by conf lines */ dbufs_allocated = 0, /* memory used by dbufs */ dbufs_used = 0, /* memory used by dbufs */ msg_allocated = 0, /* memory used by struct Msg */ msgbuf_allocated = 0, /* memory used by struct MsgBuf */ listenersm = 0, /* memory used by listetners */ rm = 0, /* res memory used */ totcl = 0, totch = 0, totww = 0, tot = 0; count_whowas_memory(&wwu, &wwm, &wwa, &wwam); wwm += sizeof(struct Whowas) * feature_uint(FEAT_NICKNAMEHISTORYLENGTH); wwm += sizeof(struct Whowas *) * WW_MAX; for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { c++; if (MyConnect(acptr)) { cn++; for (link = cli_confs(acptr); link; link = link->next) lcc++; } if (cli_user(acptr)) { for (inv = cli_user(acptr)->invited; inv; inv = inv->next_user) usi++; for (member = cli_user(acptr)->channel; member; member = member->next_channel) ++memberships; if (cli_user(acptr)->away) { aw++; awm += (strlen(cli_user(acptr)->away) + 1); } } #if defined(UNDERNET) if (IsAccount(acptr)) acc++; #endif } cm = c * sizeof(struct Client); cnm = cn * sizeof(struct Connection); user_count_memory(&us, &usm); for (chptr = GlobalChannelList; chptr; chptr = chptr->next) { ch++; chm += (strlen(chptr->chname) + sizeof(struct Channel)); for (ban = chptr->banlist; ban; ban = ban->next) { chb++; chbm += strlen(ban->who) + strlen(ban->banstr) + 2 + sizeof(*ban); } } for (aconf = GlobalConfList; aconf; aconf = aconf->next) { co++; com += aconf->host ? strlen(aconf->host) + 1 : 0; com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0; com += aconf->name ? strlen(aconf->name) + 1 : 0; com += sizeof(struct ConfItem); } for (cltmp = get_class_list(); cltmp; cltmp = cltmp->next) cl++; #if defined(USE_SSL) send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients %d(%zu) Connections %d(%zu) SSL %d", c, cm, cn, cnm, ssl_count()); #else send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients %d(%zu) Connections %d(%zu)", c, cm, cn, cnm); #endif send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Users %zu(%zu) Accounts %d(%zu) Invites %d(%zu)", us, usm, acc, acc * (ACCOUNTLEN + 1), usi, usi * sizeof(struct Invite)); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":User channels %d(%zu) Aways %d(%zu)", memberships, memberships * sizeof(struct Membership), aw, awm); totcl = cm + cnm + us * sizeof(struct User) + memberships * sizeof(struct Membership) + awm; totcl += lcc * sizeof(struct SLink) + usi * sizeof(struct SLink); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Conflines %d(%zu) Attached %d(%zu) Classes %d(%zu)", co, com, lcc, lcc * sizeof(struct SLink), cl, cl * sizeof(struct ConnectionClass)); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Channels %d(%zu) Bans %d(%zu)", ch, chm, chb, chbm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Channel Members %d(%zu)", memberships, memberships * sizeof(struct Membership)); totch = chm + chbm; send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Whowas Users %d(%zu) Away %d(%zu) Array %u(%zu)", wwu, wwu * sizeof(struct User), wwa, wwam, feature_uint(FEAT_NICKNAMEHISTORYLENGTH), wwm); totww = wwu * sizeof(struct User) + wwam + wwm; watch_count_memory(&wt, &wtm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Watchs %d(%zu)", wt, wtm); motd_memory_count(cptr); gl = gline_memory_count(&glm); ju = jupe_memory_count(&jum); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Hash: client %d(%zu), chan is the same", HASHSIZE, sizeof(void *) * HASHSIZE); count_listener_memory(&listeners, &listenersm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Listeners allocated %d(%zu)", listeners, listenersm); #if defined(DDB) ddb_count_memory(&dbs, &dbm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":DDB keys allocated %d(%zu)", dbs, dbm); #endif /* * NOTE: this count will be accurate only for the exact instant that this * message is being sent, so the count is affected by the dbufs that * are being used to send this message out. If this is not desired, move * the dbuf_count_memory call to a place before we start sending messages * and cache DBufAllocCount and DBufUsedCount in variables until they * are sent. */ dbuf_count_memory(&dbufs_allocated, &dbufs_used); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":DBufs allocated %u(%zu) used %u(%zu)", DBufAllocCount, dbufs_allocated, DBufUsedCount, dbufs_used); /* The DBuf caveats now count for this, but this routine now sends * replies all on its own. */ msgq_count_memory(cptr, &msg_allocated, &msgbuf_allocated); rm = cres_mem(cptr); tot = totww + totch + totcl + com + cl * sizeof(struct ConnectionClass) + dbufs_allocated + msg_allocated + msgbuf_allocated + rm; tot += sizeof(void *) * HASHSIZE * 3; #if defined(MDEBUG) send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Allocations: %zu(%zu)", fda_get_block_count(), fda_get_byte_count()); #endif send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Total: ww %zu ch %zu cl %zu co %zu db %zu ms %zu mb %zu", totww, totch, totcl, com, dbufs_allocated, msg_allocated, msgbuf_allocated); }
int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; struct Channel *ch2ptr; struct Client *c2ptr; struct Membership* member; char* s; char* para = parc > 1 ? parv[1] : 0; int showingdelayed = 0; if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { para = (parc > 2) ? parv[2] : 0; showingdelayed = NAMES_DEL; if (parc > 3 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %s %C", 3, parc, parv)) return 0; } else if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) return 0; if (EmptyString(para)) { send_reply(sptr, RPL_ENDOFNAMES, "*"); return 0; } do { s = strchr(para, ','); if (s) *s++ = '\0'; /* * Special Case 1: "/names 0". * Full list as per RFC. */ if ((*para == '0') || (*para == '\0')) { int idx; int mlen; int flag; struct Channel *ch3ptr; char buf[BUFSIZE]; mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); /* List all visible channels/visible members */ for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next) { if (!ShowChannel(sptr, ch2ptr)) continue; /* Don't show secret chans. */ else if (find_channel_member(sptr, ch2ptr)) do_names(sptr, ch2ptr, showingdelayed|NAMES_ALL); /* Full list if we're in this chan. */ else do_names(sptr, ch2ptr, showingdelayed|NAMES_VIS); } /* List all remaining users on channel '*' */ strcpy(buf, "* * :"); idx = 5; flag = 0; for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr)) { int showflag = 0; if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr))) continue; member = cli_user(c2ptr)->channel; while (member) { ch3ptr = member->channel; if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr)) showflag = 1; member = member->next_channel; } if (showflag) /* Have we already shown them? */ continue; strcpy(buf + idx, cli_name(c2ptr)); idx += strlen(cli_name(c2ptr)); buf[idx++] = ' '; flag = 1; if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */ { send_reply(sptr, RPL_NAMREPLY, buf); strcpy(buf, "* * :"); idx = 5; flag = 0; } } if (flag) send_reply(sptr, RPL_NAMREPLY, buf); send_reply(sptr, RPL_ENDOFNAMES, "*"); } else if ((chptr = FindChannel(para)) != NULL) { member = find_member_link(chptr, sptr); if (member) { /* * Special Case 2: User is on this channel, requesting full names list. * (As performed with each /join) - ** High frequency usage ** */ do_names(sptr, chptr, showingdelayed|NAMES_ALL|NAMES_EON); } else { /* * Special Case 3: User isn't on this channel, show all visible users, in * non secret channels. */ do_names(sptr, chptr, showingdelayed|NAMES_VIS|NAMES_EON); } } else send_reply(sptr, RPL_ENDOFNAMES, para); } while ((para = s) != NULL); return 1; }
static void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr; struct Client *acptr2; const struct ConnectionClass* cl; char* tname; int doall; int *link_s; int *link_u; int cnt = 0; int wilds; int dow; if (parc < 2 || BadPtr(parv[1])) { /* just "TRACE" without parameters. Must be from local client */ parc = 1; acptr = &me; tname = cli_name(&me); i = HUNTED_ISME; } else if (parc < 3 || BadPtr(parv[2])) { /* No target specified. Make one before propagating. */ parc = 2; tname = parv[1]; if ((acptr = find_match_server(parv[1])) || ((acptr = FindClient(parv[1])) && !MyUser(acptr))) { if (IsUser(acptr)) parv[2] = cli_name(cli_user(acptr)->server); else parv[2] = cli_name(acptr); parc = 3; parv[3] = 0; if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr), "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; } else i = HUNTED_ISME; } else { /* Got "TRACE <tname> :<target>" */ parc = 3; if (MyUser(sptr) || Protocol(cptr) < 10) acptr = find_match_server(parv[2]); else acptr = FindNServer(parv[2]); if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; tname = parv[1]; } if (i == HUNTED_PASS) { if (!acptr) acptr = next_client(GlobalClientList, tname); else acptr = cli_from(acptr); send_reply(sptr, RPL_TRACELINK, version, debugmode, tname, acptr ? cli_name(cli_from(acptr)) : "<No_match>"); return; } doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; /* Don't give (long) remote listings to lusers */ if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) { send_reply(sptr, RPL_TRACEEND); return; } link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0])); link_u = link_s + maxconnections; if (doall) { for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (IsUser(acptr)) link_u[cli_fd(cli_from(acptr))]++; else if (IsServer(acptr)) link_s[cli_fd(cli_from(acptr))]++; } } /* report all direct connections */ for (i = 0; i <= HighestFd; i++) { const char *conClass; if (!(acptr = LocalClientArray[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && match(tname, cli_name(acptr))) continue; if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr))) continue; conClass = get_client_class(acptr); switch (cli_status(acptr)) { case STAT_CONNECTING: send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr)); cnt++; break; case STAT_HANDSHAKE: send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr)); cnt++; break; case STAT_ME: break; case STAT_UNKNOWN: case STAT_UNKNOWN_USER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, get_client_name(acptr, HIDE_IP)); cnt++; break; case STAT_UNKNOWN_SERVER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server"); cnt++; break; case STAT_USER: /* Only opers see users if there is a wildcard but anyone can see all the opers. */ if ((IsAnOper(sptr) && (MyUser(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) send_reply(sptr, RPL_TRACEOPERATOR, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); else send_reply(sptr, RPL_TRACEUSER, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); cnt++; } break; /* * Connection is a server * * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age> * * class Class the server is in * nS Number of servers reached via this link * nC Number of clients reached via this link * name Name of the server linked * ConnBy Who established this link * last Seconds since we got something from this link * age Seconds this link has been alive * * Additional comments etc...... -Cym-<*****@*****.**> */ case STAT_SERVER: if (cli_serv(acptr)->user) { if (!cli_serv(acptr)->by[0] || !(acptr2 = findNUser(cli_serv(acptr)->by)) || (cli_user(acptr2) != cli_serv(acptr)->user)) acptr2 = NULL; send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), acptr2 ? cli_name(acptr2) : "*", cli_serv(acptr)->user->username, cli_serv(acptr)->user->host, CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); } else send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*", cli_name(&me), CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); cnt++; break; default: /* We actually shouldn't come here, -msa */ send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP)); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (IsAnOper(sptr) && doall) { for (cl = get_class_list(); cl; cl = cl->next) { if (Links(cl) > 1) send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1); } } send_reply(sptr, RPL_TRACEEND); MyFree(link_s); }
/** Send a (prefixed) command to all users matching \a to as \a who. * @warning \a pattern must not contain %v. * @param[in] from Source of the command. * @param[in] cmd Long name of command. * @param[in] tok Short name of command. * @param[in] to Destination host/server mask. * @param[in] one Client direction to skip (or NULL). * @param[in] who Type of match for \a to (either MATCH_HOST or MATCH_SERVER). * @param[in] pattern Format string for command arguments. */ void sendcmdto_match(struct Client *from, const char *cmd, const char *tok, const char *to, struct Client *one, unsigned int who, const char *pattern, ...) { struct VarData vd; struct irc_in_addr addr; struct Client *cptr; struct MsgBuf *user_mb; struct MsgBuf *serv_mb; unsigned char nbits; vd.vd_format = pattern; /* See if destination looks like an IP mask. */ if (!ipmask_parse(to, &addr, &nbits)) nbits = 255; /* Build buffer to send to users */ va_start(vd.vd_args, pattern); /* TODO-ZOLTAN: Revisar el tema de Globales if (IsUser(from) && IsService(cli_user(from)->server)) */ user_mb = msgq_make(0, "%:#C %s %v", from, cmd, &vd); /* else { char *mask, *msg; mask = (char *)va_arg(vd.vd_args, char *); msg = (char *)va_arg(vd.vd_args, char *); user_mb = msgq_make(0, "%:#C %s :*** Global Message -> (%s): %s", from, cmd, mask, msg); } */ va_end(vd.vd_args); /* Build buffer to send to servers */ va_start(vd.vd_args, pattern); serv_mb = msgq_make(&me, "%C %s %v", from, tok, &vd); va_end(vd.vd_args); /* send buffer along */ bump_sentalong(one); for (cptr = GlobalClientList; cptr; cptr = cli_next(cptr)) { if (cli_sentalong(cptr) == sentalong_marker || !IsRegistered(cptr) || IsServer(cptr) || !match_it(from, cptr, to, &addr, nbits, who) || cli_fd(cli_from(cptr)) < 0) continue; /* skip it */ cli_sentalong(cptr) = sentalong_marker; if (MyConnect(cptr)) /* send right buffer */ send_buffer(cptr, user_mb, 0); else send_buffer(cptr, serv_mb, 0); } msgq_clean(user_mb); msgq_clean(serv_mb); }