/* * 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; }
/** Register numeric of new (remote) client. * See @ref numnicks for more details. * Add it to the appropriate client_list. * @param[in] acptr %User being registered. * @param[in] yxx User's numnick. */ void SetRemoteNumNick(struct Client* acptr, const char *yxx) { struct Client** acptrp; struct Client* server = cli_user(acptr)->server; if (5 == strlen(yxx)) { strcpy(cli_yxx(acptr), yxx + 2); } else { (cli_yxx(acptr))[0] = *++yxx; (cli_yxx(acptr))[1] = *++yxx; (cli_yxx(acptr))[2] = 0; } Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr), base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask)); acptrp = &(cli_serv(server))->client_list[base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask]; if (*acptrp) { /* * this exits the old client in the array, not the client * that is being set */ exit_client(cli_from(acptr), *acptrp, server, "Numeric nick collision (Ghost)"); } *acptrp = acptr; }
/* * mo_asll - oper message handler */ int mo_asll(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; int hits; int i; if (parc < 2) return need_more_params(sptr, "ASLL"); if (parc == 2 && MyUser(sptr)) parv[parc++] = cli_name(&me); if (hunt_server_prio_cmd(sptr, CMD_ASLL, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME) return 0; mask = parv[1]; for (i = hits = 0; i <= HighestFd; i++) { acptr = LocalClientArray[i]; if (!acptr || !IsServer(acptr) || !MyConnect(acptr) || match(mask, cli_name(acptr))) continue; send_asll_reply(&me, sptr, cli_name(acptr), cli_serv(acptr)->asll_rtt, cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from); hits++; } sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :AsLL for %s: %d local servers matched", sptr, mask, hits); return 0; }
/** * Removes all clients and downlinks (+clients) of any server * QUITs are generated and sent to local users. * @param cptr server that must have all dependents removed * @param sptr source who thought that this was a good idea * @param comment comment sent as sign off message to local clients */ static void exit_downlinks(struct Client *cptr, struct Client *sptr, char *comment) { struct Client *acptr; struct DLink *next; struct DLink *lp; struct Client **acptrp; int i; /* Run over all its downlinks */ for (lp = cli_serv(cptr)->down; lp; lp = next) { next = lp->next; acptr = lp->value.cptr; /* Remove the downlinks and client of the downlink */ exit_downlinks(acptr, sptr, comment); /* Remove the downlink itself */ exit_one_client(acptr, cli_name(&me)); } /* Remove all clients of this server */ acptrp = cli_serv(cptr)->client_list; for (i = 0; i <= cli_serv(cptr)->nn_mask; ++acptrp, ++i) { if (*acptrp) exit_one_client(*acptrp, comment); } }
/* * mr_error - unregistered client message handler * * parv[0] = sender prefix * parv[parc-1] = text */ int mr_error(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char *para; if (!IsHandshake(cptr) && !IsConnecting(cptr)) return 0; /* ignore ERROR from regular clients */ para = (parc > 1 && *parv[parc - 1] != '\0') ? parv[parc - 1] : "<>"; Debug((DEBUG_ERROR, "Received ERROR message from %s: %s", cli_name(sptr), para)); if (cptr == sptr) sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C -- %s", cptr, para); else sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C via %C -- %s", sptr, cptr, para); if (cli_serv(sptr)) { MyFree(cli_serv(sptr)->last_error_msg); DupString(cli_serv(sptr)->last_error_msg, para); } return 0; }
/** Send a server notice out across the network before sending to all * users subscribing to the indicated \a mask except for \a one. * @param[in] from Client TOK_SNO is sent from. * @param[in] mask One of the SNO_* constants. * @param[in] pattern Format string for server notice. */ void sendto_opmask_butone_global(struct Client *one, unsigned int mask, const char *pattern, ...) { va_list vl; struct VarData vd; struct MsgBuf *mb; struct DLink *lp; va_start(vl, pattern); if (cli_serv(&me) && (lp = cli_serv(&me)->down)) { vd.vd_format = pattern; va_copy(vd.vd_args, vl); mb = msgq_make(&me, "%C " TOK_SNO " %d :%v", &me, mask, &vd); for (lp = cli_serv(&me)->down; lp; lp = lp->next) { if (one && lp->value.cptr == cli_from(one)) continue; send_buffer(lp->value.cptr, mb, 0); } msgq_clean(mb); } vsendto_opmask_butone(&me, one, mask, pattern, vl); va_end(vl); }
void checkServer(struct Client *sptr, struct Client *acptr) { char outbuf[BUFSIZE]; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s (%Tu)", myctime(acptr->cli_serv->timestamp), acptr->cli_serv->timestamp); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name); send_reply(sptr, RPL_DATASTR, outbuf); if (cli_sslclifp(acptr) && (strlen(cli_sslclifp(acptr)) > 0)) { ircd_snprintf(0, outbuf, sizeof(outbuf), "SSL Fingerprint:: %s", cli_sslclifp(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", (acptr == &me) ? UserStats.local_clients : cli_serv(acptr)->clients, base64toint(cli_serv(acptr)->nn_capacity)); send_reply(sptr, RPL_DATASTR, outbuf); if (IsBurst(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Bursting"); else if (IsBurstAck(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack"); else if (IsService(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsHub(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Hub"); ircd_snprintf(0, outbuf, sizeof(outbuf), " Class:: %s", get_client_class(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (feature_bool(FEAT_CHECK_EXTENDED)) { int dlinkc = 0; struct DLink* slink = NULL; send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_DATASTR, "Downlinks::"); for (slink = cli_serv(acptr)->down; slink; slink = slink->next) { ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc, IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ", cli_name(slink->value.cptr)); send_reply(sptr, RPL_DATASTR, outbuf); } if (!dlinkc) send_reply(sptr, RPL_DATASTR, "<none>"); } /* Send 'END OF CHECK' message */ send_reply(sptr, RPL_ENDOFCHECK, " "); }
/** Complete non-blocking connect()-sequence. Check access and * terminate connection, if trouble detected. * @param cptr Client to which we have connected, with all ConfItem structs attached. * @return Zero on failure (caller should exit_client()), non-zero on success. */ static int completed_connection(struct Client* cptr) { struct ConfItem *aconf; time_t newts; struct Client *acptr; int i; assert(0 != cptr); /* * get the socket status from the fd first to check if * connection actually succeeded */ if ((cli_error(cptr) = os_get_sockerr(cli_fd(cptr)))) { const char* msg = strerror(cli_error(cptr)); if (!msg) msg = "Unknown error"; sendto_opmask_butone(0, SNO_OLDSNO, "Connection failed to %s: %s", cli_name(cptr), msg); return 0; } if (!(aconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER))) { sendto_opmask_butone(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr)); return 0; } if (s_state(&(cli_socket(cptr))) == SS_CONNECTING) socket_state(&(cli_socket(cptr)), SS_CONNECTED); if (!EmptyString(aconf->passwd)) sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd); /* * Create a unique timestamp */ newts = TStime(); for (i = HighestFd; i > -1; --i) { if ((acptr = LocalClientArray[i]) && (IsServer(acptr) || IsHandshake(acptr))) { if (cli_serv(acptr)->timestamp >= newts) newts = cli_serv(acptr)->timestamp + 1; } } assert(0 != cli_serv(cptr)); cli_serv(cptr)->timestamp = newts; SetHandshake(cptr); /* * Make us timeout after twice the timeout for DNS look ups */ cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6n :%s", cli_name(&me), cli_serv(&me)->timestamp, newts, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me)); return (IsDead(cptr)) ? 0 : 1; }
/** Remove a client from a server's user array. * @param[in] server %Server that owns the user to remove. * @param[in] yxx Numnick of client to remove. */ void RemoveYXXClient(struct Client* server, const char* yxx) { assert(0 != server); assert(0 != yxx); if (*yxx) { Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx, base64toint(yxx) & cli_serv(server)->nn_mask)); cli_serv(server)->client_list[base64toint(yxx) & cli_serv(server)->nn_mask] = 0; } }
/** * Send a (prefixed) command to all servers matching or not matching a * flag but one. * @param[in] from Client sending the command. * @param[in] cmd Long name of command (ignored). * @param[in] tok Short name of command. * @param[in] one Client direction to skip (or NULL). * @param[in] require Only send to servers with this Flag bit set. * @param[in] forbid Do not send to servers with this Flag bit set. * @param[in] pattern Format string for command arguments. */ void sendcmdto_flag_serv_butone(struct Client *from, const char *cmd, const char *tok, struct Client *one, int require, int forbid, const char *pattern, ...) { struct VarData vd; struct MsgBuf *mb; struct DLink *lp; vd.vd_format = pattern; /* set up the struct VarData for %v */ va_start(vd.vd_args, pattern); /* use token */ mb = msgq_make(&me, "%C %s %v", from, tok, &vd); va_end(vd.vd_args); /* send it to our downlinks */ for (lp = cli_serv(&me)->down; lp; lp = lp->next) { if (one && lp->value.cptr == cli_from(one)) continue; if ((require < FLAG_LAST_FLAG) && !HasFlag(lp->value.cptr, require)) continue; if ((forbid < FLAG_LAST_FLAG) && HasFlag(lp->value.cptr, forbid)) continue; send_buffer(lp->value.cptr, mb, 0); } msgq_clean(mb); }
/** * Send a (prefixed) command to all servers but one. * @param[in] from Client sending the command. * @param[in] cmd Long name of command (ignored). * @param[in] tok Short name of command. * @param[in] one Client direction to skip (or NULL). * @param[in] pattern Format string for command arguments. */ void sendcmdto_serv(struct Client *from, const char *cmd, const char *tok, struct Client *one, const char *pattern, ...) { struct VarData vd; struct MsgBuf *mb; struct DLink *lp; vd.vd_format = pattern; /* set up the struct VarData for %v */ va_start(vd.vd_args, pattern); /* use token */ mb = msgq_make(&me, "%C %s %v", from, tok, &vd); va_end(vd.vd_args); /* canonicalize 'one' pointer */ if (one) one = cli_from(one); /* send it to our downlinks */ for (lp = cli_serv(&me)->down; lp; lp = lp->next) { if (lp->value.cptr == one) continue; send_buffer(lp->value.cptr, mb, 0); } msgq_clean(mb); }
/* * 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); }
/** Set a server's numeric nick. * @param[in] cptr %Client that announced the server (ignored). * @param[in,out] server %Server that is being assigned a numnick. * @param[in] yxx %Numeric nickname for server. */ void SetServerYXX(struct Client* cptr, struct Client* server, const char* yxx) { unsigned int index; if (5 == strlen(yxx)) { ircd_strncpy(cli_yxx(server), yxx, 2); ircd_strncpy(cli_serv(server)->nn_capacity, yxx + 2, 3); } else { (cli_yxx(server))[0] = yxx[0]; cli_serv(server)->nn_capacity[0] = yxx[1]; cli_serv(server)->nn_capacity[1] = yxx[2]; } cli_serv(server)->nn_mask = base64toint(cli_serv(server)->nn_capacity); index = base64toint(cli_yxx(server)); if (index >= lastNNServer) lastNNServer = index + 1; server_list[index] = server; /* Note, exit_one_client uses the fact that `client_list' != NULL to * determine that SetServerYXX has been called - and then calls * ClearServerYXX. However, freeing the allocation happens in free_client() */ cli_serv(server)->client_list = (struct Client**) MyCalloc(cli_serv(server)->nn_mask + 1, sizeof(struct Client*)); }
/** Attach CONF_UWORLD items to a server and everything attached to it. */ static void attach_conf_uworld(struct Client *cptr) { struct DLink *lp; attach_confs_byhost(cptr, cli_name(cptr), CONF_UWORLD); for (lp = cli_serv(cptr)->down; lp; lp = lp->next) attach_conf_uworld(lp->value.cptr); }
/* * ms_pong - server message handler * * parv[0] = sender prefix * parv[1] = origin * parv[2] = destination */ int ms_pong(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* origin; char* destination; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if (parc < 2 || EmptyString(parv[1])) { return protocol_violation(sptr,"No Origin on PONG"); } origin = parv[1]; destination = parv[2]; ClearPingSent(cptr); ClearPingSent(sptr); cli_lasttime(cptr) = CurrentTime; if (parc > 5) { /* AsLL pong */ cli_serv(cptr)->asll_rtt = atoi(militime_float(parv[3])); cli_serv(cptr)->asll_to = atoi(parv[4]); cli_serv(cptr)->asll_from = atoi(militime_float(parv[5])); cli_serv(cptr)->asll_last = CurrentTime; return 0; } if (EmptyString(destination)) return 0; if (*destination == '!') { /* AsLL ping reply from a non-AsLL server */ cli_serv(cptr)->asll_rtt = atoi(militime_float(destination + 1)); } else if (0 != ircd_strcmp(destination, cli_name(&me))) { struct Client* acptr; if ((acptr = FindClient(destination))) sendcmdto_one(sptr, CMD_PONG, acptr, "%s %s", origin, destination); } return 0; }
/** Allocate a new Server object for a client. * If Client::cli_serv == NULL, allocate a Server structure for it and * initialize it. * @param[in] cptr %Client to make into a server. * @return The value of cli_serv(\a cptr). */ struct Server *make_server(struct Client *cptr) { struct Server *serv = cli_serv(cptr); assert(cli_verify(cptr)); if (!serv) { serv = (struct Server*) MyMalloc(sizeof(struct Server)); assert(0 != serv); memset(serv, 0, sizeof(struct Server)); /* All variables are 0 by default */ servs.inuse++; servs.alloc++; cli_serv(cptr) = serv; cli_serv(cptr)->lag = 60000; *serv->by = '\0'; DupString(serv->last_error_msg, "<>"); /* String must be non-empty */ } return cli_serv(cptr); }
/* * 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; }
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)); } }
/** Send a server map to a client. * @param[in] cptr Client to who to send the map. * @param[in] server Top-level server to display. * @param[in] mask Mask to filter which servers are shown. * @param[in] prompt_length Number of characters used in prompt. */ static void dump_map(struct Client *cptr, struct Client *server, char *mask, int prompt_length) { const char *chr; static char prompt[64]; struct DLink *lp; char *p = prompt + prompt_length; int cnt = 0; *p = '\0'; if (prompt_length > 60) send_reply(cptr, RPL_MAPMORE, prompt, cli_name(server)); else { char lag[512]; if (cli_serv(server)->lag>10000) lag[0]=0; else if (cli_serv(server)->lag<0) strcpy(lag,"(0s)"); else sprintf(lag,"(%is)",cli_serv(server)->lag); if (IsBurst(server)) chr = "*"; else if (IsBurstAck(server)) chr = "!"; else chr = ""; send_reply(cptr, RPL_MAP, prompt, chr, cli_name(server), lag, (server == &me) ? UserStats.local_clients : cli_serv(server)->clients); } if (prompt_length > 0) { p[-1] = ' '; if (p[-2] == '`') p[-2] = ' '; } if (prompt_length > 60) return; strcpy(p, "|-"); for (lp = cli_serv(server)->down; lp; lp = lp->next) if (match(mask, cli_name(lp->value.cptr))) ClrFlag(lp->value.cptr, FLAG_MAP); else { SetFlag(lp->value.cptr, FLAG_MAP); cnt++; } for (lp = cli_serv(server)->down; lp; lp = lp->next) { if (!HasFlag(lp->value.cptr, FLAG_MAP)) continue; if (--cnt == 0) *p = '`'; dump_map(cptr, lp->value.cptr, mask, prompt_length + 2); } if (prompt_length > 0) p[-1] = '-'; }
/* * 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); }
/** Update server start timestamps and TS offsets. * @param[in] cptr Server that just connected. * @param[in] timestamp Current time according to \a cptr. * @param[in] start_timestamp Time that \a cptr started. * @param[in] recv_time Current time as we know it. */ static void check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time) { Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu", cli_serv(&me)->timestamp, start_timestamp)); Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; " "difference %ld", recv_time, timestamp, timestamp - recv_time)); if (feature_bool(FEAT_RELIABLE_CLOCK)) { if (start_timestamp < cli_serv(&me)->timestamp) cli_serv(&me)->timestamp = start_timestamp; if (IsUnknown(cptr)) cli_serv(cptr)->timestamp = TStime(); } else if (start_timestamp < cli_serv(&me)->timestamp) { sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: " "%Tu < %Tu", start_timestamp, cli_serv(&me)->timestamp); cli_serv(&me)->timestamp = start_timestamp; TSoffset += timestamp - recv_time; sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", (int)(timestamp - recv_time)); } else if ((start_timestamp > cli_serv(&me)->timestamp) && IsUnknown(cptr)) { cli_serv(cptr)->timestamp = TStime(); } else if (timestamp != recv_time) { /* * Equal start times, we have a collision. Let the connected-to * server decide. This assumes leafs issue more than half of the * connection attempts. */ if (IsUnknown(cptr)) cli_serv(cptr)->timestamp = TStime(); else if (IsHandshake(cptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", (int)(timestamp - recv_time)); TSoffset += timestamp - recv_time; } } }
/* * ms_asll - server message handler */ int ms_asll(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; int hits; int i; if (parc < 2) return need_more_params(sptr, "ASLL"); if (parc > 5) { if (!(acptr = findNUser(parv[1]))) return 0; if (MyUser(acptr)) send_asll_reply(sptr, acptr, parv[2], atoi(parv[3]), atoi(parv[4]), atoi(parv[5])); else sendcmdto_prio_one(sptr, CMD_ASLL, acptr, "%C %s %s %s %s", acptr, parv[2], parv[3], parv[4], parv[5]); return 0; } if (hunt_server_prio_cmd(sptr, CMD_ASLL, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME) return 0; mask = parv[1]; for (i = hits = 0; i <= HighestFd; i++) { acptr = LocalClientArray[i]; if (!acptr || !IsServer(acptr) || !MyConnect(acptr) || match(mask, cli_name(acptr))) continue; sendcmdto_prio_one(&me, CMD_ASLL, sptr, "%C %s %i %i %i", sptr, cli_name(acptr), cli_serv(acptr)->asll_rtt, cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from); hits++; } sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :AsLL for %s: %d local servers matched", sptr, mask, hits); return 0; }
/** 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); }
/** Register numeric of new (local) client. * See @ref numnicks for more details. * Assign a numnick and add it to our client_list. * @param[in] cptr %User being registered. */ int SetLocalNumNick(struct Client *cptr) { static unsigned int last_nn = 0; struct Client** client_list = cli_serv(&me)->client_list; unsigned int mask = cli_serv(&me)->nn_mask; unsigned int count = 0; assert(cli_user(cptr)->server == &me); while (client_list[last_nn & mask]) { if (++count == NN_MAX_CLIENT) { assert(count < NN_MAX_CLIENT); return 0; } if (++last_nn == NN_MAX_CLIENT) last_nn = 0; } client_list[last_nn & mask] = cptr; /* Reserve the numeric ! */ inttobase64(cli_yxx(cptr), last_nn, 3); if (++last_nn == NN_MAX_CLIENT) last_nn = 0; return 1; }
/** Set a server's capacity. * @param[in] c %Server whose capacity is being set. * @param[in] capacity Maximum number of clients the server supports. */ void SetYXXCapacity(struct Client* c, unsigned int capacity) { unsigned int max_clients = 16; /* * Calculate mask to be used for the maximum number of clients */ while (max_clients < capacity) max_clients <<= 1; /* * Sanity checks */ if (max_clients > NN_MAX_CLIENT) { fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d " "too large ! Please decrease this value.\n", max_clients - NN_MAX_CLIENT); exit(-1); } --max_clients; inttobase64(cli_serv(c)->nn_capacity, max_clients, 3); cli_serv(c)->nn_mask = max_clients; /* Our Numeric Nick mask */ cli_serv(c)->client_list = (struct Client**) MyCalloc(max_clients + 1, sizeof(struct Client*)); server_list[base64toint(cli_yxx(c))] = c; }
/* * ms_squit (server) * * parv[0] = sender prefix * parv[1] = server name * parv[2] = timestamp * parv[parc-1] = comment * * No longer supports wildcards from servers. * No longer squits a server that gave us an malformed squit message. * - Isomer 1999-12-18 * */ int ms_squit(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char* server = parv[1]; struct Client *acptr; time_t timestamp = 0; char *comment = 0; if (parc < 2) return need_more_params(sptr, "SQUIT"); comment = parv[parc-1]; if (BadPtr(parv[parc - 1])) comment = cli_name(sptr); acptr = FindServer(server); if (!acptr) acptr = FindNServer(server); if (!acptr) { Debug((DEBUG_NOTICE, "Ignoring SQUIT to an unknown server")); return 0; } /* If they are squitting me, we reverse it */ if (IsMe(acptr)) acptr = cptr; /* Bugfix by Prefect */ if (parc > 2) timestamp = atoi(parv[2]); else protocol_violation(cptr, "SQUIT with no timestamp/reason"); /* If atoi(parv[2]) == 0 we must indeed squit ! * It will be our neighbour. */ if ( timestamp != 0 && timestamp != cli_serv(acptr)->timestamp) { Debug((DEBUG_NOTICE, "Ignoring SQUIT with the wrong timestamp")); return 0; } return exit_client(cptr, acptr, sptr, comment); }
/** Look up a user by numnick string. * See @ref numnicks for more details. * @param[in] yxx %Numeric nickname of user. * @return %User with that numnick (or NULL). */ struct Client* findNUser(const char* yxx) { struct Client* server = 0; if (5 == strlen(yxx)) { if (0 != (server = FindXNServer(yxx))) { Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, base64toint(yxx + 2) & cli_serv(server)->nn_mask)); return cli_serv(server)->client_list[base64toint(yxx + 2) & cli_serv(server)->nn_mask]; } } else if (0 != (server = FindNServer(yxx))) { Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, base64toint(yxx + 1) & cli_serv(server)->nn_mask)); return cli_serv(server)->client_list[base64toint(yxx + 1) & cli_serv(server)->nn_mask]; } return 0; }
/** Close the connection with the highest sendq. * This should be called when we need to free buffer memory. * @param[in] servers_too If non-zero, consider killing servers, too. */ void kill_highest_sendq(int servers_too) { int i; unsigned int highest_sendq = 0; struct Client *highest_client = 0; for (i = HighestFd; i >= 0; i--) { if (!LocalClientArray[i] || (!servers_too && cli_serv(LocalClientArray[i]))) continue; /* skip servers */ /* If this sendq is higher than one we last saw, remember it */ if (MsgQLength(&(cli_sendQ(LocalClientArray[i]))) > highest_sendq) { highest_client = LocalClientArray[i]; highest_sendq = MsgQLength(&(cli_sendQ(highest_client))); } } if (highest_client) dead_link(highest_client, "Buffer allocation error"); }
/** Handle a CREATE message from a server. * Atomically creates a new channel and ops the creator. * * \a parv has the following elements: * \li \a parv[1] Comma-separated list of channels to create. * \li \a parv[2] Creation timestamp for the channel(s). * * 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 ms_create(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { time_t chanTS; /* channel creation time */ char *p; /* strtok state */ char *name; /* channel name */ struct Channel *chptr; /* channel */ struct JoinBuf join; /* join and create buffers */ struct JoinBuf create; struct ModeBuf mbuf; /* a mode buffer */ int badop; /* a flag */ if (IsServer(sptr)) return protocol_violation(sptr,"%s tried to CREATE a channel", cli_name(sptr)); /* sanity checks: Only accept CREATE messages from servers */ if (parc < 3 || *parv[2] == '\0') return need_more_params(sptr,"CREATE"); chanTS = atoi(parv[2]); /* A create that didn't appear during a burst has that servers idea of * the current time. Use it for lag calculations. */ if (!IsBurstOrBurstAck(sptr) && 0 != chanTS) cli_serv(cli_user(sptr)->server)->lag = TStime() - chanTS; /* If this server is >1 minute fast, warn */ if (TStime() - chanTS<-60) { static time_t rate; sendto_opmask_ratelimited(0, SNO_NETWORK, &rate, "Timestamp drift from %C (%is); issuing " "SETTIME to correct this", cli_user(sptr)->server, chanTS - TStime()); /* Now issue a SETTIME to resync. If we're in the wrong, our * (RELIABLE_CLOCK) hub will bounce a SETTIME back to us. */ sendcmdto_prio_one(&me, CMD_SETTIME, cli_user(sptr)->server, "%Tu %C", TStime(), cli_user(sptr)->server); } joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, chanTS); /* For each channel in the comma separated list: */ for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, 0, ",")) { badop = 0; if (IsLocalChannel(name)) continue; if ((chptr = FindChannel(name))) { /* Is the remote server confused? */ if (find_member_link(chptr, sptr)) { protocol_violation(sptr, "%s tried to CREATE a channel already joined", cli_name(sptr)); continue; } /* Check if we need to bounce a mode */ if (TStime() - chanTS > TS_LAG_TIME || (chptr->creationtime && chanTS > chptr->creationtime && /* Accept CREATE for zannels. This is only really necessary on a network with servers prior to 2.10.12.02: we just accept their TS and ignore the fact that it was a zannel. The influence of this on a network that is completely 2.10.12.03 or higher is neglectable: Normally a server only sends a CREATE after first sending a DESTRUCT. Thus, by receiving a CREATE for a zannel one of three things happened: 1. The DESTRUCT was sent during a net.break; this could mean that our zannel is at the verge of expiring too, it should have been destructed. It is correct to copy the newer TS now, all modes already have been reset, so it will be as if it was destructed and immediately recreated. In order to avoid desyncs of modes, we don't accept a CREATE for channels that have +A set. 2. The DESTRUCT passed, then someone created the channel on our side and left it again. In this situation we have a near simultaneous creation on two servers; the person on our side already left within the time span of a message propagation. The channel will therefore be less than 48 hours old and no 'protection' is necessary. 3. The source server sent the CREATE while linking, before it got the BURST for our zannel. If this happens, we should reset the channel back to the old timestamp. This can be distinguished from case #1 by checking IsBurstOrBurstAck(cli_user(sptr)->server). */ #if defined(UNDERNET) !(chptr->users == 0 && !chptr->mode.apass[0]))) { #else !(chptr->users == 0))) { #endif if (!IsBurstOrBurstAck(cli_user(sptr)->server)) { modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_SERVER | /* Send mode to server */ MODEBUF_DEST_HACK2 | /* Send a HACK(2) message */ MODEBUF_DEST_BOUNCE)); /* And bounce the mode */ #if defined(UNDERNET) modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, MAXOPLEVEL + 1); #else modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, 0); #endif modebuf_flush(&mbuf); badop = 1; } else if (chanTS > chptr->creationtime + 4) { /* If their handling of the BURST will lead to deopping the * user, have the user join without getting ops (if the * server's handling of the BURST keeps their ops, the channel * will use our timestamp). */ badop = 1; } if (badop) joinbuf_join(&join, chptr, 0); } } else /* Channel doesn't exist: create it */ chptr = get_channel(sptr, name, CGT_CREATE); if (!badop) { /* Set (or correct) our copy of the TS */ chptr->creationtime = chanTS; joinbuf_join(&create, chptr, CHFL_CHANOP); } }
/** Send a (prefixed) WALL of type \a type to all users except \a one. * @warning \a pattern must not contain %v. * @param[in] from Source of the command. * @param[in] type One of WALL_DESYNCH, WALL_WALLOPS or WALL_WALLUSERS. * @param[in] one Client direction to skip (or NULL). * @param[in] pattern Format string for command arguments. */ void sendwallto_group_butone(struct Client *from, int type, struct Client *one, const char *pattern, ...) { struct VarData vd; struct Client *cptr; struct MsgBuf *mb; struct DLink *lp; char *prefix=NULL; char *tok=NULL; int his_wallops; int i; vd.vd_format = pattern; /* Build buffer to send to users */ va_start(vd.vd_args, pattern); switch (type) { case WALL_DESYNCH: prefix=""; tok=TOK_DESYNCH; break; case WALL_WALLOPS: prefix="* "; tok=TOK_WALLOPS; break; case WALL_WALLUSERS: prefix="$ "; tok=TOK_WALLUSERS; break; default: assert(0); } mb = msgq_make(0, "%:#C " MSG_WALLOPS " :%s%v", from, prefix,&vd); va_end(vd.vd_args); /* send buffer along! */ his_wallops = feature_bool(FEAT_HIS_WALLOPS); for (i = 0; i <= HighestFd; i++) { if (!(cptr = LocalClientArray[i]) || (cli_fd(cli_from(cptr)) < 0) || (type == WALL_DESYNCH && !SendDebug(cptr)) || (type == WALL_WALLOPS && (!SendWallops(cptr) || (his_wallops && !IsAnOper(cptr)))) || (type == WALL_WALLUSERS && !SendWallops(cptr))) continue; /* skip it */ send_buffer(cptr, mb, 1); } msgq_clean(mb); /* Build buffer to send to servers */ va_start(vd.vd_args, pattern); mb = msgq_make(&me, "%C %s :%v", from, tok, &vd); va_end(vd.vd_args); /* send buffer along! */ for (lp = cli_serv(&me)->down; lp; lp = lp->next) { if (one && lp->value.cptr == cli_from(one)) continue; send_buffer(lp->value.cptr, mb, 1); } msgq_clean(mb); }