void PosixSelectorBase::TryConnecting(Watched* watched) { OP_ASSERT(IsConnecting(watched)); OpStatus::Ignore(ConnectSocketAddress(watched->m_address, watched->m_fd, watched->m_listener)); if (!watched->m_address && IsConnecting(watched)) { Log(VERBOSE, "%010p: Connected %d with listener %010p.\n", this, watched->m_fd, watched->m_listener); watched->Out(); watched->Into(&m_watched); } }
/* * 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; }
static void CheckMissing(struct servercstruct *server) { int delay; if(!IsConnecting(server->sinfo)&&(server->nexttry<=currtime) ) { if(server->server!=NULL) { DelServer(server->server); server->server=NULL; } if(botinfo->maxtries != 0 && server->conatt >= botinfo->maxtries) { NotConnectable(server->sinfo); return; } server->conatt++; delay=CalcConDelay(server->conatt); server->nexttry=currtime+delay; info_log("Connecting to %s attempt %i next attempt in %i seconds\n",server->address, server->conatt, delay); AddServer(server->sinfo, server->address, botinfo->nick); /* convert botinfo->timout (minutes) to sinfo->timeout (seconds) */ server->sinfo->timeout=botinfo->timeout * 60; //disconnectonidle(server->sinfo); connect_to_host(server->sinfo,server->address,server->port); } }
/* * m_close - CLOSE message handler * - added by Darren Reed Jul 13 1992. */ int m_close(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client* acptr; int i; int closed = 0; if (!HasUmode(sptr,UMODE_KILL)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } for (i = highest_fd; i; i--) { if (!(acptr = local[i])) continue; if (!IsUnknown(acptr) && !IsConnecting(acptr) && !IsHandshake(acptr)) continue; sendto_one(sptr, form_str(RPL_CLOSING), me.name, parv[0], get_client_name(acptr, SHOW_IP), acptr->status); (void)exit_client(acptr, acptr, acptr, "Oper Closing"); closed++; } sendto_one(sptr, form_str(RPL_CLOSEEND), me.name, parv[0], closed); return 0; }
void report_fds(aClient *cptr) { int i; char *name, *blocking; for(i = 0; i < MAXCONNECTIONS; i++) { if(fd_list[i].type == FDT_NONE) continue; if(fd_list[i].type == FDT_CLIENT || fd_list[i].type == FDT_AUTH) { aClient *cptr = (aClient *) fd_list[i].value; int hide = (IsConnecting(cptr) || IsHandshake(cptr) || IsServer(cptr)) ? HIDEME : 0; name = get_client_name(cptr, hide); blocking = (cptr->flags & FLAGS_BLOCKED) ? "BLOCKED" : "_"; } else { name = "-"; blocking = "-"; } sendto_one(&me, cptr, ":%s %d %s :%d - %s [%s] %s %s", me.name, RPL_STATSDEBUG, cptr->name, i, type_string(fd_list[i].type), flags_string(fd_list[i].flags), name, blocking); } }
RemoteStatus NetworkClient::WaitLogin() { const int check_count = 50; const double sleep_time = connect_timeout_/check_count; for (int x = 0; IsConnecting() && x < check_count; ++x) { Thread::Sleep(sleep_time); } RemoteStatus status = kRemoteUnknown; if (socket_ == 0) { status = kRemoteNoConnection; } else { cure::Packet* _packet = GetPacketFactory()->Allocate(); for (int x = 0; x < 100000 && IsLoggingIn(); ++x) { ReceiveStatus received = ReceiveNonBlocking(_packet); switch (received) { case cure::NetworkAgent::kReceiveOk: { const size_t message_count = _packet->GetMessageCount(); if (message_count >= 1) { cure::Message* message = _packet->GetMessageAt(0); if (message->GetType() == kMessageTypeStatus) { cure::MessageStatus* message_status = (cure::MessageStatus*)message; status = message_status->GetRemoteStatus(); SetLoginAccountId(message_status->GetInteger()); } } } break; case cure::NetworkAgent::kReceiveParseError: { log_.Error("Problem with receiving crap data during login wait!"); status = kRemoteLoginErronousData; SetLoginAccountId(0); } break; case cure::NetworkAgent::kReceiveConnectionBroken: { log_.Error("Disconnected from server while waiting for login!"); status = kRemoteUnknown; SetLoginAccountId(0); } break; case cure::NetworkAgent::kReceiveNoData: { // Nothing, really. } break; } if (IsLoggingIn()) { Thread::Sleep(0.05); } } GetPacketFactory()->Release(_packet); } StopLoginThread(); return (status); }
/* handle_command() * * inputs - pointer to message block * - pointer to client * - pointer to client message is from * - count of number of args * - pointer to argv[] array * output - -1 if error from server * side effects - */ static void handle_command(struct Message *mptr, struct Client *client_p, struct Client *from, unsigned int i, char *hpara[MAXPARA]) { MessageHandler handler = 0; if (IsServer(client_p)) mptr->rcount++; mptr->count++; /* New patch to avoid server flooding from unregistered connects * - Pie-Man 07/27/2000 */ if (!IsRegistered(client_p)) { /* if its from a possible server connection * ignore it.. more than likely its a header thats sneaked through */ if ((IsHandshake(client_p) || IsConnecting(client_p) || IsServer(client_p)) && !(mptr->flags & MFLG_UNREG)) return; } handler = mptr->handlers[client_p->handler]; /* check right amount of params is passed... --is */ if (i < mptr->parameters) { if (!IsServer(client_p)) { sendto_one(client_p, form_str(ERR_NEEDMOREPARAMS), me.name, EmptyString(hpara[0]) ? "*" : hpara[0], mptr->cmd); } else { sendto_gnotice_flags(UMODE_ALL, L_ALL, me.name, &me, NULL, "Dropping server %s due to (invalid) command '%s' " "with only %d arguments (expecting %d).", client_p->name, mptr->cmd, i, mptr->parameters); ilog(L_CRIT, "Insufficient parameters (%d) for command '%s' from %s.", i, mptr->cmd, client_p->name); exit_client(client_p, client_p, "Not enough arguments to server command."); } } else (*handler)(client_p, from, i, hpara); }
static void server_action(int action, struct socket_info *sinfo) { if (action == NET_READ) { if (getline(sinfo) == 1) parse_server(sinfo); return; } if (action == NET_WRITE) { if (IsConnecting(sinfo)) { NotConnecting(sinfo); SetConnected(sinfo); NotWriteable(sinfo); register_to_server(sinfo); } return; } }
/* * Note: At least at protocol level ERROR has only one parameter. * --msa * * parv[0] = command * parv[*] = parameters */ static int mr_error(struct Client *source_p, int parc, char *parv[]) { const char *para = (parc > 1 && !EmptyString(parv[1])) ? parv[1] : "<>"; assert(MyConnect(source_p)); if (!IsHandshake(source_p) && !IsConnecting(source_p)) return 0; ilog(LOG_TYPE_IRCD, "Received ERROR message from %s: %s", source_p->name, para); sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, "ERROR :from %s -- %s", get_client_name(source_p, HIDE_IP), para); sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, "ERROR :from %s -- %s", get_client_name(source_p, MASK_IP), para); return 0; }
/* 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); }
/* ** send_queued ** This function is called from the main select-loop (or whatever) ** when there is a chance the some output would be possible. This ** attempts to empty the send queue as far as possible... */ int send_queued(aClient *to) { char *msg; int len, rlen, more = 0; aClient *bysptr = NULL; /* ** Once socket is marked dead, we cannot start writing to it, ** even if the error is removed... */ if (IsDead(to)) { /* ** Actually, we should *NEVER* get here--something is ** not working correct if send_queued is called for a ** dead socket... --msa */ return -1; } #ifdef ZIP_LINKS /* ** Here, we must make sure than nothing will be left in to->zip->outbuf ** This buffer needs to be compressed and sent if all the sendQ is sent */ if ((to->flags & FLAGS_ZIP) && to->zip->outcount) { if (DBufLength(&to->sendQ) > 0) more = 1; else { msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s", get_client_name(to, FALSE)); } } } #endif while (DBufLength(&to->sendQ) > 0 || more) { msg = dbuf_map(&to->sendQ, &len); /* Returns always len > 0 */ if ((rlen = deliver_it(to, msg, len)) < 0) { if ( (IsConnecting(to) || IsHandshake(to)) && to->serv && to->serv->byuid[0]) { bysptr = find_uid(to->serv->byuid, NULL); if (bysptr && !MyConnect(bysptr)) { sendto_one(bysptr, ":%s NOTICE %s :" "Write error (%s) to %s, closing link", ME, bysptr->name, strerror(-rlen), to->name); } } return dead_link(to, "Write error (%s) to %s, closing link", strerror(-rlen), get_client_name(to, FALSE)); } (void)dbuf_delete(&to->sendQ, rlen); to->lastsq = DBufLength(&to->sendQ)/1024; if (rlen < len) /* ..or should I continue until rlen==0? */ break; #ifdef ZIP_LINKS if (DBufLength(&to->sendQ) == 0 && more) { /* ** The sendQ is now empty, compress what's left ** uncompressed and try to send it too */ more = 0; msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s", get_client_name(to, FALSE)); } } #endif } return (IsDead(to)) ? -1 : 0; }
/** Check whether the introduction of a new server would cause a loop * or be disallowed by leaf and hub configuration directives. * @param[in] cptr Neighbor who sent the message. * @param[in] sptr Client that originated the message. * @param[out] ghost If non-NULL, receives ghost timestamp for new server. * @param[in] host Name of new server. * @param[in] numnick Numnick mask of new server. * @param[in] timestamp Claimed link timestamp of new server. * @param[in] hop Number of hops to the new server. * @param[in] junction Non-zero if the new server is still bursting. * @return CPTR_KILLED if \a cptr was SQUIT. 0 if some other server * was SQUIT. 1 if the new server is allowed. */ static int check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, unsigned int hop, int junction) { struct Client* acptr; struct Client* LHcptr = NULL; struct ConfItem* lhconf; enum lh_type active_lh_line = ALLOWED; int ii; if (ghost) *ghost = 0; /* * Calculate type of connect limit and applicable config item. */ lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER); assert(lhconf != NULL); if (ghost) { if (!feature_bool(FEAT_HUB)) for (ii = 0; ii <= HighestFd; ii++) if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) { active_lh_line = I_AM_NOT_HUB; break; } } else if (hop > lhconf->maximum) { /* Because "maximum" should be 0 for non-hub links, check whether * there is a hub mask -- if not, complain that the server isn't * allowed to hub. */ active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB; } else if (lhconf->hub_limit && match(lhconf->hub_limit, host)) { struct Client *ac3ptr; active_lh_line = NOT_ALLOWED_TO_HUB; if (junction) for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (IsJunction(ac3ptr)) { LHcptr = ac3ptr; break; } } /* * We want to find IsConnecting() and IsHandshake() too, * use FindClient(). * The second finds collisions with numeric representation of existing * servers - these shouldn't happen anymore when all upgraded to 2.10. * -- Run */ while ((acptr = FindClient(host)) || (numnick && (acptr = FindNServer(numnick)))) { /* * This link is trying feed me a server that I already have * access through another path * * Do not allow Uworld to do this. * Do not allow servers that are juped. * Do not allow servers that have older link timestamps * then this try. * Do not allow servers that use the same numeric as an existing * server, but have a different name. * * If my ircd.conf sucks, I can try to connect to myself: */ if (acptr == &me) return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host); /* * Detect wrong numeric. */ if (0 != ircd_strcmp(cli_name(acptr), host)) { sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":SERVER Numeric Collision: %s != %s", cli_name(acptr), host); return exit_client_msg(cptr, cptr, &me, "NUMERIC collision between %s and %s." " Is your server numeric correct ?", host, cli_name(acptr)); } /* * Kill our try, if we had one. */ if (IsConnecting(acptr)) { if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me, "Just connected via another link") == CPTR_KILLED) return CPTR_KILLED; /* * We can have only ONE 'IsConnecting', 'IsHandshake' or * 'IsServer', because new 'IsConnecting's are refused to * the same server if we already had it. */ break; } /* * Avoid other nick collisions... * This is a doubtful test though, what else would it be * when it has a server.name ? */ else if (!IsServer(acptr) && !IsHandshake(acptr)) return exit_client_msg(cptr, cptr, &me, "Nickname %s already exists!", host); /* * Our new server might be a juped server: */ else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4))) { if (!IsServer(sptr)) return exit_client(cptr, sptr, &me, cli_info(acptr)); sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":Received :%s SERVER %s from %s !?!", NumServ(cptr), host, cli_name(cptr)); return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr)); } /* * Of course we find the handshake this link was before :) */ else if (IsHandshake(acptr) && acptr == cptr) break; /* * Here we have a server nick collision... * We don't want to kill the link that was last /connected, * but we neither want to kill a good (old) link. * Therefor we kill the second youngest link. */ if (1) { struct Client* c2ptr = 0; struct Client* c3ptr = acptr; struct Client* ac2ptr; struct Client* ac3ptr; /* Search youngest link: */ for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; if (IsServer(sptr)) { for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; } if (timestamp > cli_serv(c3ptr)->timestamp) { c3ptr = 0; c2ptr = acptr; /* Make sure they differ */ } /* Search second youngest link: */ for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; if (IsServer(sptr)) { for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; } if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = 0; /* If timestamps are equal, decide which link to break * by name. */ if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) == (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp)) { const char *n2, *n2up, *n3, *n3up; if (c2ptr) { n2 = cli_name(c2ptr); n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up); } else { n2 = host; n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (c3ptr) { n3 = cli_name(c3ptr); n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up); } else { n3 = host; n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (strcmp(n2, n2up) > 0) n2 = n2up; if (strcmp(n3, n3up) > 0) n3 = n3up; if (strcmp(n3, n2) > 0) { ac2ptr = c2ptr; c2ptr = c3ptr; c3ptr = ac2ptr; } } /* Now squit the second youngest link: */ if (!c2ptr) return exit_new_server(cptr, sptr, host, timestamp, "server %s already exists and is %ld seconds younger.", host, (long)cli_serv(acptr)->timestamp - (long)timestamp); else if (cli_from(c2ptr) == cptr || IsServer(sptr)) { struct Client *killedptrfrom = cli_from(c2ptr); if (active_lh_line != ALLOWED) { /* * If the L: or H: line also gets rid of this link, * we sent just one squit. */ if (LHcptr && a_kills_b_too(LHcptr, c2ptr)) break; /* * If breaking the loop here solves the L: or H: * line problem, we don't squit that. */ if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) active_lh_line = ALLOWED; else { /* * If we still have a L: or H: line problem, * we prefer to squit the new server, solving * loop and L:/H: line problem with only one squit. */ LHcptr = 0; break; } } /* * If the new server was introduced by a server that caused a * Ghost less then 20 seconds ago, this is probably also * a Ghost... (20 seconds is more then enough because all * SERVER messages are at the beginning of a net.burst). --Run */ if (CurrentTime - cli_serv(cptr)->ghost < 20) { killedptrfrom = cli_from(acptr); if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) return CPTR_KILLED; } else if (exit_client_msg(cptr, c2ptr, &me, "Loop <-- %s (new link is %ld seconds younger)", host, (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) - (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED) return CPTR_KILLED; /* * Did we kill the incoming server off already ? */ if (killedptrfrom == cptr) return 0; } else { if (active_lh_line != ALLOWED) { if (LHcptr && a_kills_b_too(LHcptr, acptr)) break; if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) active_lh_line = ALLOWED; else { LHcptr = 0; break; } } /* * We can't believe it is a lagged server message * when it directly connects to us... * kill the older link at the ghost, rather then * at the second youngest link, assuming it isn't * a REAL loop. */ if (ghost) *ghost = CurrentTime; /* Mark that it caused a ghost */ if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) return CPTR_KILLED; break; } } } if (active_lh_line != ALLOWED) { if (!LHcptr) LHcptr = sptr; if (active_lh_line == MAX_HOPS_EXCEEDED) { return exit_client_msg(cptr, LHcptr, &me, "Maximum hops exceeded for %s at %s", cli_name(cptr), host); } else if (active_lh_line == NOT_ALLOWED_TO_HUB) { return exit_client_msg(cptr, LHcptr, &me, "%s is not allowed to hub for %s", cli_name(cptr), host); } else /* I_AM_NOT_HUB */ { ServerStats->is_ref++; return exit_client(cptr, LHcptr, &me, "I'm a leaf, define the HUB feature"); } } return 1; }
int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Message* mptr; struct Client* acptr; char l_stat = parc > 1 ? parv[1][0] : '\0'; int i; int doall = 0; int wilds = 0; int ignore_request = 0; int valid_stats = 0; char* name; char *mename = me.name; if(sptr->user && sptr->user->vlink) mename = sptr->user->vlink->name; if(!IsClient(sptr)) { return 0; } if(!IsAnOper(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), mename, parv[0]); return -1; } if (hunt_server(cptr,sptr,":%s STATS %s :%s",2,parc,parv)!=HUNTED_ISME) return 0; if (parc > 2) { name = parv[2]; if (!irccmp(name, mename)) doall = 2; else if (match(name, mename)) doall = 1; if (strchr(name, '*') || strchr(name, '?')) wilds = 1; } else name = mename; switch (l_stat) { case 'L' : case 'l' : /* * send info about connections which match, or all if the * mask matches mename. Only restrictions are on those who * are invisible not being visible to 'foreigners' who use * a wild card based search to list it. */ for (i = 0; i <= highest_fd; i++) { if (!(acptr = local[i])) continue; if (!doall && wilds && !match(name, acptr->name)) continue; if (!(doall || wilds) && irccmp(name, acptr->name)) continue; /* I've added a sanity test to the "CurrentTime - acptr->since" * occasionally, acptr->since is larger than CurrentTime. * The code in parse.c "randomly" increases the "since", * which means acptr->since is larger then CurrentTime at times, * this gives us very high odd number.. * So, I am going to return 0 for ->since if this happens. * - Dianora */ /* trust opers not on this server */ /* if(IsAnOper(sptr)) */ /* Don't trust opers not on this server */ #ifdef HIDE_SERVERS_IPS if(MyClient(sptr) && IsAnOper(sptr) && !IsServer(acptr) && !IsConnecting(acptr) && !IsHandshake(acptr)) #else if(MyClient(sptr) && IsAnOper(sptr)) #endif { sendto_one(sptr, Lformat, mename, RPL_STATSLINKINFO, parv[0], get_client_name(acptr, TRUE), (int)DBufLength(&acptr->sendQ), (int)acptr->sendM, (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK, CurrentTime - acptr->firsttime, (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0, IsServer(acptr) ? show_capabilities(acptr) : "-"); } else { if(IsServer(acptr)) sendto_one(sptr, Lformat, mename, RPL_STATSLINKINFO, parv[0], get_client_name(acptr, HIDEME), (int)DBufLength(&acptr->sendQ), (int)acptr->sendM, (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK, CurrentTime - acptr->firsttime, (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0, IsServer(acptr) ? show_capabilities(acptr) : "-"); else sendto_one(sptr, Lformat, mename, RPL_STATSLINKINFO, parv[0], (IsUpper(lstat)) ? get_client_name(acptr, TRUE) : get_client_name(acptr, FALSE), (int)DBufLength(&acptr->sendQ), (int)acptr->sendM, (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK, CurrentTime - acptr->firsttime, (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0, IsServer(acptr) ? show_capabilities(acptr) : "-"); } } valid_stats++; break; case 'C' : case 'c' : report_configured_links(sptr, CONF_CONNECT_SERVER|CONF_NOCONNECT_SERVER); valid_stats++; break; case 'B' : case 'b' : sendto_one(sptr,":%s NOTICE %s :Use stats I instead", mename, parv[0]); break; case 'D': report_dlines(sptr); valid_stats++; break; case 'd': report_domain_stats(sptr); valid_stats++; break; case 'E' : case 'e' : sendto_one(sptr,":%s NOTICE %s :Use stats I instead", mename, parv[0]); break; case 'F' : case 'f' : report_channel_stats(sptr); break; case 'g' : report_sxlines(sptr); valid_stats++; break; case 'G' : report_glines(sptr, NULL); valid_stats++; break; case 'H' : case 'h' : report_configured_links(sptr, CONF_HUB|CONF_LEAF); valid_stats++; break; case 'i' : report_vlinks(sptr); valid_stats++; break; case 'I' : report_mtrie_conf_links(sptr, CONF_CLIENT); valid_stats++; break; case 'k' : report_temp_klines(sptr); valid_stats++; break; case 'K' : if(parc > 3) report_matching_host_klines(sptr,parv[3]); else report_mtrie_conf_links(sptr, CONF_KILL); valid_stats++; break; case 'M' : case 'm' : for (mptr = msgtab; mptr->cmd; mptr++) sendto_one(sptr, form_str(RPL_STATSCOMMANDS), mename, parv[0], mptr->cmd, mptr->count, mptr->bytes); valid_stats++; break; case 'o' : case 'O' : report_configured_links(sptr, CONF_OPS); valid_stats++; break; case 'P' : show_ports(sptr); valid_stats++; break; case 'p' : show_opers(sptr); valid_stats++; break; case 'Q' : report_qlines(sptr); valid_stats++; break; case 'q' : report_sqlines(sptr); valid_stats++; break; case 'R' : case 'r' : send_usage(sptr,parv[0]); valid_stats++; break; case 'S' : list_scache(cptr,sptr,parc,parv); valid_stats++; break; case 'T' : case 't' : tstats(sptr, parv[0]); valid_stats++; break; case 'U' : report_specials(sptr,CONF_ULINE,RPL_STATSULINE); valid_stats++; break; case 'u' : { time_t now; now = CurrentTime - me.since; sendto_one(sptr, form_str(RPL_STATSUPTIME), mename, parv[0], now/86400, (now/3600)%24, (now/60)%60, now%60); sendto_one(sptr, form_str(RPL_STATSCONN), mename, parv[0], MaxConnectionCount, MaxClientCount, Count.totalrestartcount); valid_stats++; break; } case 'V' : show_servers(sptr); valid_stats++; break; case 'v' : report_svlines(sptr); valid_stats++; break; case 'x' : report_specials(sptr,CONF_XLINE,RPL_STATSXLINE); valid_stats++; break; case 'X' : report_configured_links(sptr, CONF_MISSING); valid_stats++; break;; case 'Y' : case 'y' : report_classes(sptr); valid_stats++; break; case 'Z' : report_zlines(sptr); valid_stats++; break; case 'z' : count_memory(sptr, parv[0]); valid_stats++; break; case '?': serv_info(sptr, parv[0]); valid_stats++; break; default : l_stat = '*'; break; } sendto_one(sptr, form_str(RPL_ENDOFSTATS), mename, parv[0], l_stat); /* personally, I don't see why opers need to see stats requests * at all. They are just "noise" to an oper, and users can't do * any damage with stats requests now anyway. So, why show them? * -Dianora */ if (valid_stats) { if ( (l_stat == 'L') || (l_stat == 'l') ) { sendto_ops_imodes(IMODE_SPY, "STATS %c requested by %s (%s@%s) [%s] on %s%s", l_stat, sptr->name, sptr->username, sptr->host, sptr->user->server, parc > 2 ? parv[2] : "<no recipient>", ignore_request > 0 ? " [request ignored]" : "\0" ); } else { sendto_ops_imodes(IMODE_SPY, "STATS %c requested by %s (%s@%s) [%s]%s", l_stat, sptr->name, sptr->username, sptr->host, sptr->user->server, ignore_request > 0 ? " [request ignored]" : "\0" ); } } return 0; }
/* * parse a buffer. * * NOTE: parse() should not be called recusively by any other functions! */ int parse(aClient *cptr, char *pbuffer, char *bufend) { aClient *from = cptr; char *ch; char *s; size_t i; char* numeric = 0; unsigned int paramcount; struct Message *mptr; Debug((DEBUG_DEBUG, "Parsing %s: %s", get_client_name(cptr, TRUE), pbuffer)); if (IsDead(cptr)) return -1; s = sender; *s = '\0'; for (ch = pbuffer; *ch == ' '; ch++) /* skip spaces */ /* null statement */ ; para[0] = from->name; if (*ch == ':') { ch++; /* ** Copy the prefix to 'sender' assuming it terminates ** with SPACE (or NULL, which is an error, though). */ for (i = 0; *ch && *ch != ' '; i++ ) { if (i < (sizeof(sender)-1)) *s++ = *ch; /* leave room for NULL */ ch++; } *s = '\0'; i = 0; /* ** Actually, only messages coming from servers can have ** the prefix--prefix silently ignored, if coming from ** a user client... ** ** ...sigh, the current release "v2.2PL1" generates also ** null prefixes, at least to NOTIFY messages (e.g. it ** puts "sptr->nickname" as prefix from server structures ** where it's null--the following will handle this case ** as "no prefix" at all --msa (": NOTICE nick ...") */ if (*sender && IsServer(cptr)) { from = find_client(sender, (aClient *) NULL); if (!from || !match(from->name, sender)) from = find_server(sender); para[0] = sender; /* Hmm! If the client corresponding to the * prefix is not found--what is the correct * action??? Now, I will ignore the message * (old IRC just let it through as if the * prefix just wasn't there...) --msa */ if (!from) { sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Unknown prefix (%s) from (%s)", sender, cptr->name); Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)", sender, pbuffer, cptr->name)); ServerStats->is_unpf++; remove_unknown(cptr, sender, pbuffer); return -1; } if (from->from != cptr) { ServerStats->is_wrdi++; Debug((DEBUG_ERROR, "Message (%s) coming from (%s)", buffer, cptr->name)); sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Message coming from (%s), but %s is at %s", cptr->name, from->name, from->from->name); return cancel_clients(cptr, from, pbuffer); } } while (*ch == ' ') ch++; } if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cptr->name, from->name)); if (IsServer(cptr)) sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Empty message from %s:%s", cptr->name, from->name); return(-1); } /* ** Extract the command code from the packet. Point s to the end ** of the command code and calculate the length using pointer ** arithmetic. Note: only need length for numerics and *all* ** numerics must have parameters and thus a space after the command ** code. -avalon * * ummm???? - Dianora */ if( *(ch + 3) == ' ' && /* ok, lets see if its a possible numeric.. */ IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2)) ) { mptr = (struct Message *)NULL; numeric = ch; paramcount = MAXPARA; ServerStats->is_num++; s = ch + 3; /* I know this is ' ' from above if */ *s++ = '\0'; /* blow away the ' ', and point s to next part */ } else { s = strchr(ch, ' '); /* moved from above,now need it here */ if (s) *s++ = '\0'; mptr = tree_parse(ch); if (!mptr || !mptr->cmd) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin * * Same thing goes for remote commands. If their local * server accepted it, then we should not be sending * a warning message. */ if (pbuffer[0] != '\0') { if (IsPerson(from) && MyClient(from)) sendto_one(from, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); Debug((DEBUG_ERROR,"Unknown (%s) from %s", ch, get_client_name(cptr, TRUE))); } ServerStats->is_unco++; return(-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; /* Allow only 1 msg per 2 seconds * (on average) to prevent dumping. * to keep the response rate up, * bursts of up to 5 msgs are allowed * -SRB * Opers can send 1 msg per second, burst of ~20 * -Taner */ if ((mptr->flags & 1) && !(IsServer(cptr))) { #ifdef NO_OPER_FLOOD #ifndef TRUE_NO_OPER_FLOOD /* note: both have to be defined for the real no-flood */ if (NoFloodProtection(cptr)) /* "randomly" (weighted) increase the since */ cptr->since += (cptr->receiveM % 5) ? 1 : 0; else #else if (!NoFloodProtection(cptr)) #endif #endif cptr->since += (2 + i / 120); } } /* ** Must the following loop really be so devious? On ** surface it splits the message to parameters from ** blank spaces. But, if paramcount has been reached, ** the rest of the message goes into this last parameter ** (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ /* ZZZ hmmmmmmmm whats this then? */ #if 0 if (me.user) para[0] = sender; #endif i = 1; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { while(*s == ' ') /* tabs are not considered space */ *s++ = '\0'; if(!*s) break; if (*s == ':') { /* ** The rest is single parameter--can ** include blanks also. */ para[i++] = s + 1; break; } else { para[i++] = s; if (i > paramcount) { break; } /* scan for end of string, either ' ' or '\0' */ while (IsNonEOS(*s)) s++; } } } para[i] = NULL; if (mptr == (struct Message *)NULL) return (do_numeric(numeric, cptr, from, i, para)); mptr->count++; /* patch to avoid server flooding from unregistered connects */ /* check allow_unregistered_use flag I've set up instead of function comparing *yech* - Dianora */ if (!IsRegistered(cptr) && !mptr->allow_unregistered_use ) { /* if its from a possible server connection * ignore it.. more than likely its a header thats sneaked through */ if(IsHandshake(cptr) || IsConnecting(cptr) || IsServer(cptr)) return -1; sendto_one(from, ":%s %d %s %s :Register first.", me.name, ERR_NOTREGISTERED, BadPtr(from->name) ? "*" : from->name, ch); return -1; } /* Silently drop the messages that can't be used in the honeypot */ if (IsHoneypot(cptr) && !mptr->allow_honeypot) return -1; /* Again, instead of function address comparing, see if * this function resets idle time as given from mptr * if IDLE_FROM_MSG is undefined, the sense of the flag is reversed. * i.e. if the flag is 0, then reset idle time, otherwise don't reset it. * * - Dianora */ if (IsRegisteredUser(from) && mptr->reset_idle) { /* If a local registered user, propagate anti-idle updates as needed */ if (MyClient(from) && from->user && ((from->user->last_sent + MAX_IDLE_DESYNC) < CurrentTime)) { #if 0 /* Note the misnamed message, this indicates that they are NOT idle */ #ifdef HAVE_LONG_LONG sendto_serv_butone(NULL, ":%s IDLE %s %.1lld", cptr->name, from->name, (long long)CurrentTime); #else sendto_serv_butone(NULL, ":%s IDLE %s %.1ld", cptr->name, from->name, (long)CurrentTime); #endif #endif from->user->last_sent = CurrentTime; } from->user->last = CurrentTime; } /* don't allow other commands while a list is blocked. since we treat them specially with respect to sendq. */ if ((IsDoingList(cptr)) && (*mptr->func != m_list)) return -1; return (*mptr->func)(cptr, from, i, para); }
/** Read a 'packet' of data from a connection and process it. Read in * 8k chunks to give a better performance rating (for server * connections). Do some tricky stuff for client connections to make * sure they don't do any flooding >:-) -avalon * @param cptr Client from which to read data. * @param socket_ready If non-zero, more data can be read from the client's socket. * @return Positive number on success, zero on connection-fatal failure, negative * if user is killed. */ static int read_packet(struct Client *cptr, int socket_ready) { unsigned int dolen = 0; unsigned int length = 0; if (socket_ready && !(IsUser(cptr) && DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))) { #if defined(USE_SSL) switch (client_recv(cptr, readbuf, sizeof(readbuf), &length)) { #else switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) { #endif case IO_SUCCESS: if (length) { cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); ClrFlag(cptr, FLAG_NONL); if (cli_lasttime(cptr) > cli_since(cptr)) cli_since(cptr) = cli_lasttime(cptr); } break; case IO_BLOCKED: break; case IO_FAILURE: cli_error(cptr) = errno; /* SetFlag(cptr, FLAG_DEADSOCKET); */ return 0; } } /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ if (length > 0 && IsServer(cptr)) return server_dopacket(cptr, readbuf, length); else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr))) return connect_dopacket(cptr, readbuf, length); else { /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its * turn comes around. */ if (length > 0 && dbuf_put(cptr, &(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); if ((DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD)) && !IsChannelService(cptr)) return exit_client(cptr, cptr, &me, "Excess Flood"); while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && (IsTrusted(cptr) || IsChannelService(cptr) || cli_since(cptr) - CurrentTime < 10)) { dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* * Devious looking...whats it do ? well..if a client * sends a *long* message without any CR or LF, then * dbuf_getmsg fails and we pull it out using this * loop which just gets the next 512 bytes and then * deletes the rest of the buffer contents. * -avalon */ if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { /* More than 512 bytes in the line - drop the input and yell * at the client. */ DBufClear(&(cli_recvQ(cptr))); send_reply(cptr, ERR_INPUTTOOLONG); } } else if (client_dopacket(cptr, dolen) == CPTR_KILLED) return CPTR_KILLED; /* * If it has become registered as a Server * then skip the per-message parsing below. */ if (IsHandshake(cptr) || IsServer(cptr)) { while (-1) { dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf)); if (dolen <= 0) return 1; else if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { DBufClear(&(cli_recvQ(cptr))); /* send_reply(cptr, ERR_INPUTTOOLONG); */ } } else if ((IsServer(cptr) && server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) || (!IsServer(cptr) && connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED)) return CPTR_KILLED; } } } /* If there's still data to process, wait 2 seconds first */ if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && !t_onqueue(&(cli_proc(cptr)))) { Debug((DEBUG_LIST, "Adding client process timer for %C", cptr)); cli_freeflag(cptr) |= FREEFLAG_TIMER; timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr), TT_RELATIVE, 2); } } return 1; } /** Start a connection to another server. * @param aconf Connect block data for target server. * @param by Client who requested the connection (if any). * @return Non-zero on success; zero on failure. */ int connect_server(struct ConfItem* aconf, struct Client* by) { struct Client* cptr = 0; assert(0 != aconf); if (aconf->dns_pending) { sendto_opmask(0, SNO_OLDSNO, "Server %s connect DNS pending", aconf->name); return 0; } Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name, ircd_ntoa(&aconf->address.addr))); if ((cptr = FindClient(aconf->name))) { if (IsServer(cptr) || IsMe(cptr)) { sendto_opmask(0, SNO_OLDSNO, "Server %s already present from %s", aconf->name, cli_name(cli_from(cptr))); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present " "from %s", by, aconf->name, cli_name(cli_from(cptr))); } return 0; } else if (IsHandshake(cptr) || IsConnecting(cptr)) { if (by && IsUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in " "progress", by, cli_name(cptr)); } return 0; } } /* * If we don't know the IP# for this host and it is a hostname and * not a ip# string, then try and find the appropriate host record. */ if (!irc_in_addr_valid(&aconf->address.addr) && !ircd_aton(&aconf->address.addr, aconf->host)) { char buf[HOSTLEN + 1]; host_from_uh(buf, aconf->host, HOSTLEN); gethost_byname(buf, connect_dns_callback, aconf); aconf->dns_pending = 1; return 0; } cptr = make_client(NULL, STAT_UNKNOWN_SERVER); /* * Copy these in so we have something for error detection. */ ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN); ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN); /* * Attach config entries to client here rather than in * completed_connection. This to avoid null pointer references */ attach_confs_byhost(cptr, aconf->host, CONF_SERVER); if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) { sendto_opmask(0, SNO_OLDSNO, "Host %s is not enabled for " "connecting: no Connect block", aconf->name); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no " "Connect block", by, aconf->name); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * attempt to connect to the server in the conf line */ if (!connect_inet(aconf, cptr)) { if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by, cli_name(cptr)); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * NOTE: if we're here we have a valid C:Line and the client should * have started the connection and stored the remote address/port and * ip address name in itself * * The socket has been connected or connect is in progress. */ make_server(cptr); if (by && IsUser(by)) { ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s", NumNick(by)); assert(0 == cli_serv(cptr)->user); cli_serv(cptr)->user = cli_user(by); cli_user(by)->refcnt++; } else { *(cli_serv(cptr))->by = '\0'; /* strcpy(cptr->serv->by, "Auto"); */ } cli_serv(cptr)->up = &me; SetConnecting(cptr); if (cli_fd(cptr) > HighestFd) HighestFd = cli_fd(cptr); LocalClientArray[cli_fd(cptr)] = cptr; Count_newunknown(UserStats); /* Actually we lie, the connect hasn't succeeded yet, but we have a valid * cptr, so we register it now. * Maybe these two calls should be merged. */ add_client_to_list(cptr); hAddClient(cptr); /* nextping = CurrentTime; */ return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ? completed_connection(cptr) : 1; } /** Find the real hostname for the host running the server (or one which * matches the server's name) and its primary IP#. Hostname is stored * in the client structure passed as a pointer. */ void init_server_identity(void) { const struct LocalConf* conf = conf_get_local(); assert(0 != conf); ircd_strncpy(cli_name(&me), conf->name, HOSTLEN); SetYXXServerName(&me, conf->numeric); } /** Process events on a client socket. * @param ev Socket event structure that has a struct Connection as * its associated data. */ static void client_sock_callback(struct Event* ev) { struct Client* cptr; struct Connection* con; char *fmt = "%s"; char *fallback = 0; assert(0 != ev_socket(ev)); assert(0 != s_data(ev_socket(ev))); con = (struct Connection*) s_data(ev_socket(ev)); assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); cptr = con_client(con); assert(0 == cptr || con == cli_connect(cptr)); switch (ev_type(ev)) { case ET_DESTROY: con_freeflag(con) &= ~FREEFLAG_SOCKET; if (!con_freeflag(con) && !cptr) free_connection(con); #if defined(USE_SSL) ssl_free(ev_socket(ev)); #endif break; case ET_CONNECT: /* socket connection completed */ if (!completed_connection(cptr) || IsDead(cptr)) fallback = cli_info(cptr); break; case ET_ERROR: /* an error occurred */ fallback = cli_info(cptr); cli_error(cptr) = ev_data(ev); /* If the OS told us we have a bad file descriptor, we should * record that for future reference. */ if (cli_error(cptr) == EBADF) cli_fd(cptr) = -1; if (s_state(&(con_socket(con))) == SS_CONNECTING) { completed_connection(cptr); /* for some reason, the os_get_sockerr() in completed_connection() * can return 0 even when ev_data(ev) indicates a real error, so * re-assign the client error here. */ cli_error(cptr) = ev_data(ev); break; } /*FALLTHROUGH*/ case ET_EOF: /* end of file on socket */ Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr), cli_error(cptr))); SetFlag(cptr, FLAG_DEADSOCKET); if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) { exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)", cli_name(cptr), cli_serv(cptr)->last_error_msg); return; } else { fmt = "Read error: %s"; fallback = "EOF from client"; } break; case ET_WRITE: /* socket is writable */ ClrFlag(cptr, FLAG_BLOCKED); if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048) list_next_channels(cptr); Debug((DEBUG_SEND, "Sending queued data to %C", cptr)); send_queued(cptr); break; case ET_READ: /* socket is readable */ if (!IsDead(cptr)) { Debug((DEBUG_DEBUG, "Reading data from %C", cptr)); if (read_packet(cptr, 1) == 0) /* error while reading packet */ fallback = "EOF from client"; } break; default: assert(0 && "Unrecognized socket event in client_sock_callback()"); break; } assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); if (fallback) { const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback; if (!msg) msg = "Unknown error"; exit_client_msg(cptr, cptr, &me, fmt, msg); } } /** Process a timer on client socket. * @param ev Timer event that has a struct Connection as its * associated data. */ static void client_timer_callback(struct Event* ev) { struct Client* cptr; struct Connection* con; assert(0 != ev_timer(ev)); assert(0 != t_data(ev_timer(ev))); assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev)); con = (struct Connection*) t_data(ev_timer(ev)); assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); cptr = con_client(con); assert(0 == cptr || con == cli_connect(cptr)); if (ev_type(ev)== ET_DESTROY) { con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */ if (!con_freeflag(con) && !cptr) free_connection(con); /* client is being destroyed */ } else { Debug((DEBUG_LIST, "Client process timer for %C expired; processing", cptr)); read_packet(cptr, 0); /* read_packet will re-add timer if needed */ } assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); }
/** Check access for a server given its name (passed in cptr struct). * Must check for all C/N lines which have a name which matches the * name given and a host which matches. A host alias which is the * same as the server name is also acceptable in the host field of a * C/N line. * @param cptr Peer server to check. * @return 0 if accepted, -1 if access denied. */ int conf_check_server(struct Client *cptr) { struct ConfItem* c_conf = NULL; struct SLink* lp; Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", cli_name(cptr), cli_sockhost(cptr))); if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) { Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr))); return -1; } lp = cli_confs(cptr); /* * We initiated this connection so the client should have a C and N * line already attached after passing through the connect_server() * function earlier. */ if (IsConnecting(cptr) || IsHandshake(cptr)) { c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER); if (!c_conf) { sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost Connect block for %s", cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } /* Try finding the Connect block by DNS name and IP next. */ if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER))) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * Attach by IP# only if all other checks have failed. * It is quite possible to get here with the strange things that can * happen when using DNS in the way the irc server does. -avalon */ if (!c_conf) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * detach all conf lines that got attached by attach_confs() */ det_confs_butmask(cptr, 0); /* * if no Connect block, then deny access */ if (!c_conf) { Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]", cli_name(cptr), cli_username(cptr), cli_sockhost(cptr))); return -1; } /* * attach the Connect block to the client structure for later use. */ attach_conf(cptr, c_conf); if (!irc_in_addr_valid(&c_conf->address.addr)) memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr)); Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cli_name(cptr), cli_sockhost(cptr))); return 0; }
static time_t check_pings(time_t currenttime) { aClient *cptr; aConfItem *aconf = (aConfItem *) NULL; int killflag, zkillflag, ping = 0, i; time_t oldest = 0; /* timeout removed, see EXPLANATION below */ char *reason, *ktype, fbuf[512]; char *errtxt = "No response from %s, closing link"; for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr)) continue; /* Note: No need to notify opers here. It's * already done when "FLAGS_DEADSOCKET" is set. */ if (cptr->flags & FLAGS_DEADSOCKET) { (void) exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket"); i--; continue; } killflag = NO; zkillflag = NO; if (rehashed) { if (zline_in_progress) { if (IsPerson(cptr)) { if ((aconf = find_zkill(cptr))) zkillflag = YES; } } else { if(IsPerson(cptr)) { if((aconf = find_kill(cptr))) killflag = YES; } } } /* Added a bit of code here to differentiate * between K and Z-lines. -ThemBones */ if (zkillflag || killflag) { ktype = zkillflag ? "Z-lined" : ((aconf->status == CONF_KILL) ? "K-lined" : "Autokilled"); if (killflag) { sendto_ops("%s active for %s", (aconf->status == CONF_KILL) ? "K-line" : "Autokill", get_client_name(cptr, FALSE)); reason = aconf->passwd ? aconf->passwd : ktype; } else { /* its a Z line */ sendto_ops("Z-line active for %s", get_client_name(cptr, FALSE)); reason = aconf->passwd ? aconf->passwd : "Z-lined"; } sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, ktype); ircsprintf(fbuf, "%s: %s", ktype, reason); (void) exit_client(cptr, cptr, &me, fbuf); i--; /* subtract out this fd so we check it again.. */ continue; } if (IsRegistered(cptr)) ping = cptr->pingval; else ping = CONNECTTIMEOUT; /* * Ok, so goto's are ugly and can be avoided here but this code * is already indented enough so I think its justified. -avalon * * justified by what? laziness? <g> * If the client pingtime is fine (ie, not larger than the client ping) * skip over all the checks below. - lucas */ if (ping < (currenttime - cptr->lasttime)) { /* * If the server hasnt talked to us in 2*ping seconds and it has * a ping time, then close its connection. If the client is a * user and a KILL line was found to be active, close this * connection too. */ if (((cptr->flags & FLAGS_PINGSENT) && ((currenttime - cptr->lasttime) >= (2 * ping))) || ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping))) { if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr))) { if (cptr->authfd >= 0) { (void) close(cptr->authfd); cptr->authfd = -1; cptr->count = 0; *cptr->buffer = '\0'; } #ifdef SHOW_HEADERS if (DoingDNS(cptr)) ssl_send(cptr, REPORT_FAIL_DNS, R_fail_dns, 0); if (DoingAuth(cptr)) ssl_send(cptr, REPORT_FAIL_ID, R_fail_id, 0); #endif Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s", get_client_name(cptr, TRUE))); del_queries((char *) cptr); ClearAuth(cptr); ClearDNS(cptr); SetAccess(cptr); cptr->since = currenttime; continue; } if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) { ircsprintf(fbuf, "from %s: %s", me.name, errtxt); sendto_gnotice(fbuf, get_client_name(cptr, HIDEME)); ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt); sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME)); } (void) exit_client(cptr, cptr, &me, "Ping timeout"); i--; /* subtract out this fd so we check it again.. */ continue; } /* don't send pings during a burst, as we send them already. */ else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST))) { /* * if we havent PINGed the connection and we havent heard from * it in a while, PING it to make sure it is still alive. */ cptr->flags |= FLAGS_PINGSENT; /* * not nice but does the job */ cptr->lasttime = currenttime - ping; sendto_one(cptr, "PING :%s", me.name); } } /* see EXPLANATION below * * timeout = cptr->lasttime + ping; * while (timeout <= currenttime) * timeout += ping; * if (timeout < oldest || !oldest) * oldest = timeout; */ /* * Check UNKNOWN connections - if they have been in this state * for > 100s, close them. */ if (IsUnknown(cptr)) if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) (void) exit_client(cptr, cptr, &me, "Connection Timed Out"); } rehashed = 0; zline_in_progress = 0; /* EXPLANATION * on a server with a large volume of clients, at any given point * there may be a client which needs to be pinged the next second, * or even right away (a second may have passed while running * check_pings). Preserving CPU time is more important than * pinging clients out at exact times, IMO. Therefore, I am going to make * check_pings always return currenttime + 9. This means that it may take * a user up to 9 seconds more than pingfreq to timeout. Oh well. * Plus, the number is 9 to 'stagger' our check_pings calls out over * time, to avoid doing it and the other tasks ircd does at the same time * all the time (which are usually done on intervals of 5 seconds or so). * - lucas * * if (!oldest || oldest < currenttime) * oldest = currenttime + PINGFREQUENCY; */ oldest = currenttime + 9; Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return oldest; }
static time_t check_pings(time_t currenttime) { register aClient *cptr; /* current local cptr being examined */ aConfItem *aconf = (aConfItem *)NULL; int ping = 0; /* ping time value from client */ int i; /* used to index through fd/cptr's */ time_t oldest = 0; /* next ping time */ time_t timeout; /* found necessary ping time */ char *reason; /* pointer to reason string */ int die_index=0; /* index into list */ char ping_time_out_buffer[64]; /* blech that should be a define */ /* of dying clients */ dying_clients[0] = (aClient *)NULL; /* mark first one empty */ /* * I re-wrote the way klines are handled. Instead of rescanning * the local[] array and calling exit_client() right away, I * mark the client thats dying by placing a pointer to its aClient * into dying_clients[]. When I have examined all in local[], * I then examine the dying_clients[] for aClient's to exit. * This saves the rescan on k-lines, also greatly simplifies the code, * * Jan 28, 1998 * -Dianora */ for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr)) continue; /* and go examine next fd/cptr */ /* ** Note: No need to notify opers here. It's ** already done when "FLAGS_DEADSOCKET" is set. */ if (cptr->flags & FLAGS_DEADSOCKET) { /* N.B. EVERY single time dying_clients[] is set * it must be followed by an immediate continue, * to prevent this cptr from being marked again for exit. * If you don't, you could cause exit_client() to be called twice * for the same cptr. i.e. bad news * -Dianora */ dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = ((cptr->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket"); dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } if (rehashed) { if(dline_in_progress) { if(IsPerson(cptr)) { if( (aconf = find_dkill(cptr)) ) /* if there is a returned aConfItem then kill it */ { sendto_ops("D-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON reason = aconf->passwd ? aconf->passwd : "D-lined"; dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "D-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } } } else { if(IsPerson(cptr)) { #ifdef GLINES if( (aconf = find_gkill(cptr)) ) { sendto_ops("G-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON reason = aconf->passwd ? aconf->passwd : "G-lined"; dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "G-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } else #endif if((aconf = find_kill(cptr))) /* if there is a returned aConfItem.. then kill it */ { sendto_ops("K-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON #ifdef K_COMMENT_ONLY reason = aconf->passwd ? aconf->passwd : "K-lined"; #else reason = (BadPtr(aconf->passwd) || !is_comment(aconf->passwd)) ? "K-lined" : aconf->passwd; #endif dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "K-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } } } } #ifdef IDLE_CHECK if (IsPerson(cptr)) { if( !IsElined(cptr) && ((timeofday - cptr->user->last) > idle_time)) { aConfItem *aconf; dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "idle exceeder"; dying_clients[die_index] = (aClient *)NULL; aconf = make_conf(); aconf->status = CONF_KILL; DupString(aconf->host, cptr->user->host); DupString(aconf->passwd, "idle exceeder" ); DupString(aconf->name, cptr->user->username); aconf->port = 0; aconf->hold = timeofday + 60; add_temp_kline(aconf); sendto_ops("Idle exceeder %s temp k-lining", get_client_name(cptr,FALSE)); continue; /* and go examine next fd/cptr */ } } #endif #ifdef REJECT_HOLD if (IsRejectHeld(cptr)) { if( timeofday > (cptr->firsttime + REJECT_HOLD_TIME) ) { dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "reject held client"; dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } } #endif #if defined(R_LINES) && defined(R_LINES_OFTEN) /* * this is used for KILL lines with time restrictions * on them - send a message to the user being killed * first. * *** Moved up above -taner *** * * Moved here, no more rflag -Dianora */ if (IsPerson(cptr) && find_restrict(cptr)) { sendto_ops("Restricting %s, closing link.", get_client_name(cptr,FALSE)); dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "you have been R-lined"; dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } #endif if (!IsRegistered(cptr)) ping = CONNECTTIMEOUT; else ping = get_client_ping(cptr); /* * Ok, so goto's are ugly and can be avoided here but this code * is already indented enough so I think its justified. -avalon */ /* if (!rflag && (ping >= currenttime - cptr->lasttime)) goto ping_timeout; */ /* * *sigh* I think not -Dianora */ if (ping < (currenttime - cptr->lasttime)) { /* * If the server hasnt talked to us in 2*ping seconds * and it has a ping time, then close its connection. * If the client is a user and a KILL line was found * to be active, close this connection too. */ if (((currenttime - cptr->lasttime) >= (2 * ping) && (cptr->flags & FLAGS_PINGSENT)) || ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping))) { if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr))) { if (cptr->authfd >= 0) { (void)close(cptr->authfd); cptr->authfd = -1; cptr->count = 0; *cptr->buffer = '\0'; } #ifdef SHOW_HEADERS if (DoingDNS(cptr)) send(cptr->fd, REPORT_FAIL_DNS, R_fail_dns, 0); else send(cptr->fd, REPORT_FAIL_ID, R_fail_id, 0); #endif Debug((DEBUG_NOTICE,"DNS/AUTH timeout %s", get_client_name(cptr,TRUE))); del_queries((char *)cptr); ClearAuth(cptr); ClearDNS(cptr); SetAccess(cptr); cptr->since = currenttime; continue; } if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) { sendto_ops("No response from %s, closing link", get_client_name(cptr, FALSE)); } /* * this is used for KILL lines with time restrictions * on them - send a messgae to the user being killed * first. * *** Moved up above -taner *** */ cptr->flags2 |= FLAGS2_PING_TIMEOUT; dying_clients[die_index++] = cptr; /* the reason is taken care of at exit time */ /* dying_clients_reason[die_index++] = "Ping timeout"; */ dying_clients[die_index] = (aClient *)NULL; /* * need to start loop over because the close can * affect the ordering of the local[] array.- avalon * ** Not if you do it right - Dianora */ continue; } else if ((cptr->flags & FLAGS_PINGSENT) == 0) { /* * if we havent PINGed the connection and we havent * heard from it in a while, PING it to make sure * it is still alive. */ cptr->flags |= FLAGS_PINGSENT; /* not nice but does the job */ cptr->lasttime = currenttime - ping; sendto_one(cptr, "PING :%s", me.name); } } /* ping_timeout: */ timeout = cptr->lasttime + ping; while (timeout <= currenttime) timeout += ping; if (timeout < oldest || !oldest) oldest = timeout; /* * Check UNKNOWN connections - if they have been in this state * for > 100s, close them. */ if (IsUnknown(cptr)) { if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) { dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "Connection Timed Out"; dying_clients[die_index] = (aClient *)NULL; continue; } } } /* Now exit clients marked for exit above. * it doesn't matter if local[] gets re-arranged now * * -Dianora */ for(die_index = 0; (cptr = dying_clients[die_index]); die_index++) { if(cptr->flags2 & FLAGS2_PING_TIMEOUT) { (void)ircsprintf(ping_time_out_buffer, "Ping timeout: %d seconds", currenttime - cptr->lasttime); (void)exit_client(cptr, cptr, &me, ping_time_out_buffer ); } else (void)exit_client(cptr, cptr, &me, dying_clients_reason[die_index]); } rehashed = 0; dline_in_progress = 0; if (!oldest || oldest < currenttime) oldest = currenttime + PINGFREQUENCY; Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return (oldest); }
/** * Exits a client of *any* type (user, server, etc) * from this server. Also, this generates all necessary prototol * messages that this exit may cause. * * This function implicitly exits all other clients depending on * this connection. * * For convenience, this function returns a suitable value for * m_function return value: * * CPTR_KILLED if (cptr == bcptr) * 0 if (cptr != bcptr) * * This function can be called in two ways: * 1) From before or in parse(), exiting the 'cptr', in which case it was * invoked as exit_client(cptr, cptr, &me,...), causing it to always * return CPTR_KILLED. * 2) Via parse from a m_function call, in which case it was invoked as * exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client * that generated the message in a way that we can assume he already * did remove acptr from memory himself (or in other cases we don't mind * because he will be delinked.) Or invoked as: * exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should * be removed. * In general: No generated SQUIT or QUIT should be sent to source link * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too). * * --Run * @param cptr Connection currently being handled by read_message. * @param victim Client being killed. * @param killer Client that made the decision to remove \a victim. * @param comment Reason for the exit. * @return CPTR_KILLED if cptr == bcptr, else 0. */ int exit_client(struct Client *cptr, struct Client* victim, struct Client* killer, const char* comment) { struct Client* acptr = 0; struct DLink *dlp; time_t on_for; char comment1[HOSTLEN + HOSTLEN + 2]; assert(killer); if (MyConnect(victim)) { SetFlag(victim, FLAG_CLOSING); if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim)) sendto_opmask_butone(0, SNO_CONNEXIT, "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>", cli_name(victim), cli_user(victim)->username, cli_user(victim)->host, comment, ircd_ntoa(&cli_ip(victim)), NumNick(victim) /* two %s's */); update_load(); on_for = CurrentTime - cli_firsttime(victim); if (IsUser(victim) || IsUserPort(victim)) auth_send_exit(victim); if (IsUser(victim)) log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s", cli_firsttime(victim), on_for, cli_user(victim)->username, cli_sockhost(victim), ircd_ntoa(&cli_ip(victim)), IsAccount(victim) ? cli_username(victim) : "0", NumNick(victim), /* two %s's */ cli_name(victim), cli_info(victim)); if (victim != cli_from(killer) /* The source knows already */ && IsClient(victim)) /* Not a Ping struct or Log file */ { if (IsServer(victim) || IsHandshake(victim)) sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment); else if (!IsConnecting(victim)) { if (!IsDead(victim)) { if (IsServer(victim)) sendcmdto_one(killer, CMD_ERROR, victim, ":Closing Link: %s by %s (%s)", cli_name(victim), cli_name(killer), comment); else sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)", cli_name(victim), cli_name(IsServer(killer) ? &his : killer), comment); } } if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) && (killer == &me || (IsServer(killer) && (strncmp(comment, "Leaf-only link", 14) || strncmp(comment, "Non-Hub link", 12))))) { /* * Note: check user == user needed to make sure we have the same * client */ if (cli_serv(victim)->user && *(cli_serv(victim))->by && (acptr = findNUser(cli_serv(victim)->by))) { if (cli_user(acptr) == cli_serv(victim)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s canceled: %s", acptr, cli_name(victim), comment); } else { /* * not right client, set by to empty string */ acptr = 0; *(cli_serv(victim))->by = '\0'; } } if (killer == &me) sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s", cli_name(victim), comment); } } /* * Close the Client connection first. */ close_connection(victim); } if (IsServer(victim)) { if (feature_bool(FEAT_HIS_NETSPLIT)) strcpy(comment1, "*.net *.split"); else { strcpy(comment1, cli_name(cli_serv(victim)->up)); strcat(comment1, " "); strcat(comment1, cli_name(victim)); } if (IsUser(killer)) sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:", (cli_user(killer)->server == victim || cli_user(killer)->server == cli_serv(victim)->up) ? "Local" : "Remote", get_client_name(killer, HIDE_IP), cli_name(cli_user(killer)->server)); else if (killer != &me && cli_serv(victim)->up != killer) sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :", cli_name(victim), IsServer(killer) ? cli_name(killer) : get_client_name(killer, HIDE_IP)); sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)", cli_serv(victim)->up, victim, comment); } /* * First generate the needed protocol for the other server links * except the source: */ for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) { if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim) { if (IsServer(victim)) sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s", cli_name(victim), cli_serv(victim)->timestamp, comment); else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED)) sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment); } } /* Then remove the client structures */ if (IsServer(victim)) exit_downlinks(victim, killer, comment1); exit_one_client(victim, comment); /* * cptr can only have been killed if it was cptr itself that got killed here, * because cptr can never have been a dependent of victim --Run */ return (cptr == victim) ? CPTR_KILLED : 0; }
/** Check for clients that have not sent a ping response recently. * Reschedules itself to run again at the appropriate time. * @param[in] ev Timer event (ignored). */ static void check_pings(struct Event* ev) { int expire = 0; int next_check = CurrentTime; int max_ping = 0; int i; assert(ET_EXPIRE == ev_type(ev)); assert(0 != ev_timer(ev)); next_check += feature_int(FEAT_PINGFREQUENCY); /* Scan through the client table */ for (i=0; i <= HighestFd; i++) { struct Client *cptr = LocalClientArray[i]; if (!cptr) continue; assert(&me != cptr); /* I should never be in the local client array! */ /* Remove dead clients. */ if (IsDead(cptr)) { exit_client(cptr, cptr, &me, cli_info(cptr)); continue; } max_ping = IsRegistered(cptr) ? client_get_ping(cptr) : feature_int(FEAT_CONNECTTIMEOUT); Debug((DEBUG_DEBUG, "check_pings(%s)=status:%s limit: %d current: %d", cli_name(cptr), IsPingSent(cptr) ? "[Ping Sent]" : "[]", max_ping, (int)(CurrentTime - cli_lasttime(cptr)))); /* If it's a server and we have not sent an AsLL lately, do so. */ if (IsServer(cptr)) { if (CurrentTime - cli_serv(cptr)->asll_last >= max_ping) { char *asll_ts; SetPingSent(cptr); cli_serv(cptr)->asll_last = CurrentTime; expire = cli_serv(cptr)->asll_last + max_ping; asll_ts = militime_float(NULL); sendcmdto_prio_one(&me, CMD_PING, cptr, "!%s %s %s", asll_ts, cli_name(cptr), asll_ts); } expire = cli_serv(cptr)->asll_last + max_ping; if (expire < next_check) next_check = expire; } /* Ok, the thing that will happen most frequently, is that someone will * have sent something recently. Cover this first for speed. * -- * If it's an unregistered client and hasn't managed to register within * max_ping then it's obviously having problems (broken client) or it's * just up to no good, so we won't skip it, even if its been sending * data to us. * -- hikari */ if ((CurrentTime-cli_lasttime(cptr) < max_ping) && IsRegistered(cptr)) { expire = cli_lasttime(cptr) + max_ping; if (expire < next_check) next_check = expire; continue; } /* Unregistered clients pingout after max_ping seconds, they don't * get given a second chance - if they were then people could not quite * finish registration and hold resources without being subject to k/g * lines */ if (!IsRegistered(cptr)) { assert(!IsServer(cptr)); /* If client authorization time has expired, ask auth whether they * should be checked again later. */ if ((CurrentTime-cli_firsttime(cptr) >= max_ping) && auth_ping_timeout(cptr)) continue; /* OK, they still have enough time left, so we'll just skip to the * next client. Set the next check to be when their time is up, if * that's before the currently scheduled next check -- hikari */ expire = cli_firsttime(cptr) + max_ping; if (expire < next_check) next_check = expire; continue; } /* Quit the client after max_ping*2 - they should have answered by now */ if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) ) { /* If it was a server, then tell ops about it. */ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) sendto_opmask_butone(0, SNO_OLDSNO, "No response from %s, closing link", cli_name(cptr)); exit_client_msg(cptr, cptr, &me, "Ping timeout"); continue; } if (!IsPingSent(cptr)) { /* If we haven't PINGed the connection and we haven't heard from it in a * while, PING it to make sure it is still alive. */ SetPingSent(cptr); /* If we're late in noticing don't hold it against them :) */ cli_lasttime(cptr) = CurrentTime - max_ping; if (IsUser(cptr)) sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me)); else sendcmdto_prio_one(&me, CMD_PING, cptr, ":%s", cli_name(&me)); } expire = cli_lasttime(cptr) + max_ping * 2; if (expire < next_check) next_check=expire; } assert(next_check >= CurrentTime); Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is", CurrentTime, next_check-CurrentTime)); timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check); }
/** Start a connection to another server. * @param aconf Connect block data for target server. * @param by Client who requested the connection (if any). * @return Non-zero on success; zero on failure. */ int connect_server(struct ConfItem* aconf, struct Client* by) { struct Client* cptr = 0; assert(0 != aconf); if (aconf->dns_pending) { sendto_opmask_butone(0, SNO_OLDSNO, "Server %s connect DNS pending", aconf->name); return 0; } Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name, ircd_ntoa(&aconf->address.addr))); if ((cptr = FindClient(aconf->name))) { if (IsServer(cptr) || IsMe(cptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "Server %s already present from %s", aconf->name, cli_name(cli_from(cptr))); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present " "from %s", by, aconf->name, cli_name(cli_from(cptr))); } return 0; } else if (IsHandshake(cptr) || IsConnecting(cptr)) { if (by && IsUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in " "progress", by, cli_name(cptr)); } return 0; } } /* * If we don't know the IP# for this host and it is a hostname and * not a ip# string, then try and find the appropriate host record. */ if (!irc_in_addr_valid(&aconf->address.addr) && !ircd_aton(&aconf->address.addr, aconf->host)) { char buf[HOSTLEN + 1]; host_from_uh(buf, aconf->host, HOSTLEN); gethost_byname(buf, connect_dns_callback, aconf); aconf->dns_pending = 1; return 0; } cptr = make_client(NULL, STAT_UNKNOWN_SERVER); /* * Copy these in so we have something for error detection. */ ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN); ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN); /* * Attach config entries to client here rather than in * completed_connection. This to avoid null pointer references */ attach_confs_byhost(cptr, aconf->host, CONF_SERVER); if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) { sendto_opmask_butone(0, SNO_OLDSNO, "Host %s is not enabled for " "connecting: no Connect block", aconf->name); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no " "Connect block", by, aconf->name); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * attempt to connect to the server in the conf line */ if (!connect_inet(aconf, cptr)) { if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by, cli_name(cptr)); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * NOTE: if we're here we have a valid C:Line and the client should * have started the connection and stored the remote address/port and * ip address name in itself * * The socket has been connected or connect is in progress. */ make_server(cptr); if (by && IsUser(by)) { ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s", NumNick(by)); assert(0 == cli_serv(cptr)->user); cli_serv(cptr)->user = cli_user(by); cli_user(by)->refcnt++; } else { *(cli_serv(cptr))->by = '\0'; /* strcpy(cptr->serv->by, "Auto"); */ } cli_serv(cptr)->up = &me; SetConnecting(cptr); if (cli_fd(cptr) > HighestFd) HighestFd = cli_fd(cptr); LocalClientArray[cli_fd(cptr)] = cptr; Count_newunknown(UserStats); /* Actually we lie, the connect hasn't succeeded yet, but we have a valid * cptr, so we register it now. * Maybe these two calls should be merged. */ add_client_to_list(cptr); hAddClient(cptr); /* nextping = CurrentTime; */ return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ? completed_connection(cptr) : 1; }
/*------------------------------------------------ called when main window received WM_TIMER. determine to start or not every second. --------------------------------------------------*/ void OnTimerSNTP(HWND hwnd, SYSTEMTIME* st) { BOOL bDo = FALSE; if(bSendingData) // while sending/receiving { char msg[80]; DWORD dif; dif = GetTickCount() - dwTickCountOnSend; if(dif >= (DWORD)nTimeout) // check timeout { wsprintf(msg, "timeout (%04d)", dif); SocketClose(GetDlgItem(hwnd, 1), msg); } return; } if(g_hGetHost) // while getting host address { DWORD dif; dif = GetTickCount() - dwTickCountOnGetHost; if(dif >= 10000) { SocketClose(GetDlgItem(hwnd, 1), "failed to get IP address"); } return; } if(!bSyncTimer) return; if(g_socket != -1 || g_hGetHost != NULL) return; if(nHourStart >= 0) { if((nHourStart <= nHourEnd && (nHourStart <= st->wHour && st->wHour <= nHourEnd)) || (nHourStart > nHourEnd && (nHourStart <= st->wHour || st->wHour <= nHourEnd))) { if(st->wHour == nHourStart && st->wMinute < nMinuteStart) return; if(st->wHour == nHourEnd && nMinuteEnd < st->wMinute) return; } else return; } if(bSyncADay) { if(st->wDay != nLastDay) { if(!bNoDial || IsConnecting()) bDo = TRUE; } } else { if(bFirst || (GetTickCount()-dwTickCount >= (DWORD)nMinutes*60*1000)) { if(!bNoDial || (st->wMinute != nLastMinute && IsConnecting())) bDo = TRUE; } } nLastMinute = st->wMinute; if(bDo) StartSyncTime(hwnd, NULL, 0); }
/* ** exit_client - This is old "m_bye". Name changed, because this is not a ** protocol function, but a general server utility function. ** ** This function exits a client of *any* type (user, server, etc) ** from this server. Also, this generates all necessary prototol ** messages that this exit may cause. ** ** 1) If the client is a local client, then this implicitly ** exits all other clients depending on this connection (e.g. ** remote clients having 'from'-field that points to this. ** ** 2) If the client is a remote client, then only this is exited. ** ** For convenience, this function returns a suitable value for ** m_function return value: ** ** CLIENT_EXITED if (client_p == source_p) ** 0 if (client_p != source_p) */ int exit_client( struct Client* client_p, /* The local client originating the * exit or NULL, if this exit is * generated by this server for * internal reasons. * This will not get any of the * generated messages. */ struct Client* source_p, /* Client exiting */ struct Client* from, /* Client firing off this Exit, * never NULL! */ const char* comment /* Reason for the exit */ ) { char comment1[HOSTLEN + HOSTLEN + 2]; dlink_node *m; fprintf(stderr, "Exiting client: %s\n", comment); if (MyConnect(source_p)) { /* DO NOT REMOVE. exit_client can be called twice after a failed * read/write. */ if(IsClosing(source_p)) return 0; SetClosing(source_p); if (source_p->flags & FLAGS_IPHASH) remove_one_ip(&source_p->localClient->ip); delete_adns_queries(source_p->localClient->dns_query); delete_identd_queries(source_p); client_flush_input(source_p); /* This source_p could have status of one of STAT_UNKNOWN, STAT_CONNECTING * STAT_HANDSHAKE or STAT_UNKNOWN * all of which are lumped together into unknown_list * * In all above cases IsRegistered() will not be true. */ if (!IsRegistered(source_p)) { m = dlinkFind(&unknown_list,source_p); if(m != NULL) { dlinkDelete(m, &unknown_list); free_dlink_node(m); } } if (IsOper(source_p)) { m = dlinkFind(&oper_list,source_p); if(m != NULL) { dlinkDelete(m, &oper_list); free_dlink_node(m); } } if (IsClient(source_p)) { Count.local--; if(IsPerson(source_p)) /* a little extra paranoia */ { m = dlinkFind(&lclient_list,source_p); if(m != NULL) { dlinkDelete(m,&lclient_list); free_dlink_node(m); } } } /* As soon as a client is known to be a server of some sort * it has to be put on the serv_list, or SJOIN's to this new server * from the connect burst will not be seen. */ if (IsServer(source_p) || IsConnecting(source_p) || IsHandshake(source_p)) { m = dlinkFind(&serv_list,source_p); if(m != NULL) { dlinkDelete(m,&serv_list); free_dlink_node(m); unset_chcap_usage_counts(source_p); } } if (IsServer(source_p)) { Count.myserver--; if(ServerInfo.hub) remove_lazylink_flags(source_p->localClient->serverMask); else uplink = NULL; } if (IsPerson(source_p)) sendto_realops_flags(FLAGS_CCONN, L_ALL, "Client exiting: %s (%s@%s) [%s] [%s]", source_p->name, source_p->username, source_p->host, comment, source_p->localClient->sockhost); log_user_exit(source_p); if (source_p->localClient->fd >= 0) { if (client_p != NULL && source_p != client_p) sendto_one(source_p, "ERROR :Closing Link: %s %s (%s)", source_p->host, source_p->name, comment); else sendto_one(source_p, "ERROR :Closing Link: %s (%s)", source_p->host, comment); } /* ** Currently only server connections can have ** depending remote clients here, but it does no ** harm to check for all local clients. In ** future some other clients than servers might ** have remotes too... ** ** Close the Client connection first and mark it ** so that no messages are attempted to send to it. ** (The following *must* make MyConnect(source_p) == FALSE!). ** It also makes source_p->from == NULL, thus it's unnecessary ** to test whether "source_p != target_p" in the following loops. */ close_connection(source_p); } if(IsServer(source_p)) { if(ConfigServerHide.hide_servers) { /* set netsplit message to "me.name *.split" to still show * that its a split, but hide the servers splitting */ ircsprintf(comment1,"%s *.split", me.name); } else { if((source_p->serv) && (source_p->serv->up)) strcpy(comment1, source_p->serv->up); else strcpy(comment1, "<Unknown>"); strcat(comment1," "); strcat(comment1, source_p->name); } remove_dependents(client_p, source_p, from, comment, comment1); if (source_p->servptr == &me) { sendto_realops_flags(FLAGS_ALL, L_ALL, "%s was connected for %d seconds. %d/%d sendK/recvK.", source_p->name, (int)(CurrentTime - source_p->firsttime), source_p->localClient->sendK, source_p->localClient->receiveK); ilog(L_NOTICE, "%s was connected for %d seconds. %d/%d sendK/recvK.", source_p->name, CurrentTime - source_p->firsttime, source_p->localClient->sendK, source_p->localClient->receiveK); } } exit_one_client(client_p, source_p, from, comment); return client_p == source_p ? CLIENT_EXITED : 0; }
bool NetworkClient::IsActive() const { return (IsConnecting() || IsLoggingIn() || GetLoginAccountId() != 0); }
/* * Check_pings_list() * * inputs - pointer to list to check * output - NONE * side effects - */ static void check_pings_list(dlink_list *list) { char scratch[32]; /* way too generous but... */ struct Client *client_p; /* current local client_p being examined */ int ping = 0; /* ping time value from client */ dlink_node *ptr, *next_ptr; for (ptr = list->head; ptr; ptr = next_ptr) { next_ptr = ptr->next; client_p = ptr->data; /* ** Note: No need to notify opers here. It's ** already done when "FLAGS_DEADSOCKET" is set. */ if (client_p->flags & FLAGS_DEADSOCKET) { /* Ignore it, its been exited already */ continue; } if (IsPerson(client_p)) { if(!IsExemptKline(client_p) && GlobalSetOptions.idletime && !IsOper(client_p) && !IsIdlelined(client_p) && ((CurrentTime - client_p->user->last) > GlobalSetOptions.idletime)) { struct ConfItem *aconf; aconf = make_conf(); aconf->status = CONF_KILL; DupString(aconf->host, client_p->host); DupString(aconf->passwd, "idle exceeder"); DupString(aconf->name, client_p->username); aconf->port = 0; aconf->hold = CurrentTime + 60; add_temp_kline(aconf); sendto_realops_flags(FLAGS_ALL, L_ALL, "Idle time limit exceeded for %s - temp k-lining", get_client_name(client_p, HIDE_IP)); (void)exit_client(client_p, client_p, &me, aconf->passwd); continue; } } if (!IsRegistered(client_p)) ping = CONNECTTIMEOUT; else ping = get_client_ping(client_p); if (ping < (CurrentTime - client_p->lasttime)) { /* * If the client/server hasnt talked to us in 2*ping seconds * and it has a ping time, then close its connection. */ if (((CurrentTime - client_p->lasttime) >= (2 * ping) && (client_p->flags & FLAGS_PINGSENT))) { if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ADMIN, "No response from %s, closing link", get_client_name(client_p, HIDE_IP)); sendto_realops_flags(FLAGS_ALL, L_OPER, "No response from %s, closing link", get_client_name(client_p, MASK_IP)); ilog(L_NOTICE, "No response from %s, closing link", get_client_name(client_p, HIDE_IP)); } (void)ircsprintf(scratch, "Ping timeout: %d seconds", (int)(CurrentTime - client_p->lasttime)); (void)exit_client(client_p, client_p, &me, scratch); continue; } else if ((client_p->flags & FLAGS_PINGSENT) == 0) { /* * if we havent PINGed the connection and we havent * heard from it in a while, PING it to make sure * it is still alive. */ client_p->flags |= FLAGS_PINGSENT; /* not nice but does the job */ client_p->lasttime = CurrentTime - ping; sendto_one(client_p, "PING :%s", me.name); } } /* ping_timeout: */ } }
static time_t check_pings(time_t currenttime) { #ifdef TIMEDKLINES static time_t lkill = 0; #endif Reg aClient *cptr; Reg int kflag = 0; aClient *bysptr = NULL; int ping = 0, i; time_t oldest = 0, timeout; char *reason = NULL; for (i = highest_fd; i >= 0; i--) { if (!(cptr = local[i]) || IsListener(cptr)) continue; #ifdef TIMEDKLINES kflag = 0; reason = NULL; /* ** Once per TIMEDKLINES seconds. ** (1 minute is minimum resolution in K-line field) */ if ((currenttime - lkill > TIMEDKLINES) && IsPerson(cptr) && !IsKlineExempt(cptr)) { kflag = find_kill(cptr, 1, &reason); } #endif ping = IsRegistered(cptr) ? cptr->ping : ACCEPTTIMEOUT; Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d a %d", cptr->name, cptr->status, ping, kflag, currenttime - cptr->lasttime)); /* * Ok, so goto's are ugly and can be avoided here but this code * is already indented enough so I think its justified. -avalon */ if (!kflag && IsRegistered(cptr) && (ping >= currenttime - cptr->lasttime)) goto ping_timeout; /* * If the server hasnt talked to us in 2*ping seconds * and it has a ping time, then close its connection. * If the client is a user and a KILL line was found * to be active, close this connection too. */ if (kflag || ((currenttime - cptr->lasttime) >= (2 * ping) && (cptr->flags & FLAGS_PINGSENT)) || (!IsRegistered(cptr) && (currenttime - cptr->firsttime) >= ping)) { if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr) || DoingXAuth(cptr))) { if (cptr->authfd >= 0) { (void)close(cptr->authfd); cptr->authfd = -1; cptr->count = 0; *cptr->buffer = '\0'; } Debug((DEBUG_NOTICE, "%s/%c%s timeout %s", (DoingDNS(cptr)) ? "DNS" : "dns", (DoingXAuth(cptr)) ? "X" : "x", (DoingAuth(cptr)) ? "AUTH" : "auth", get_client_name(cptr,TRUE))); del_queries((char *)cptr); ClearAuth(cptr); #if defined(USE_IAUTH) if (DoingDNS(cptr) || DoingXAuth(cptr)) { if (DoingDNS(cptr) && (iauth_options & XOPT_EXTWAIT)) { /* iauth wants more time */ sendto_iauth("%d d", cptr->fd); ClearDNS(cptr); cptr->lasttime = currenttime; continue; } if (DoingXAuth(cptr) && (iauth_options & XOPT_NOTIMEOUT)) { cptr->exitc = EXITC_AUTHTOUT; sendto_iauth("%d T", cptr->fd); exit_client(cptr, cptr, &me, "Authentication Timeout"); continue; } sendto_iauth("%d T", cptr->fd); SetDoneXAuth(cptr); } #endif ClearDNS(cptr); ClearXAuth(cptr); ClearWXAuth(cptr); cptr->firsttime = currenttime; cptr->lasttime = currenttime; continue; } if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) { if (cptr->serv && cptr->serv->byuid[0]) { bysptr = find_uid(cptr->serv->byuid, NULL); } /* we are interested only in *remote* opers */ if (bysptr && !MyConnect(bysptr)) { sendto_one(bysptr, ":%s NOTICE %s :" "No response from %s, closing" " link", ME, bysptr->name, get_client_name(cptr, FALSE)); } sendto_flag(SCH_NOTICE, "No response from %s closing link", get_client_name(cptr, FALSE)); } /* * this is used for KILL lines with time restrictions * on them - send a message to the user being killed * first. */ if (kflag && IsPerson(cptr)) { char buf[100]; sendto_flag(SCH_NOTICE, "Kill line active for %s", get_client_name(cptr, FALSE)); cptr->exitc = EXITC_KLINE; if (!BadPtr(reason)) sprintf(buf, "Kill line active: %.80s", reason); (void)exit_client(cptr, cptr, &me, (reason) ? buf : "Kill line active"); } else { cptr->exitc = EXITC_PING; (void)exit_client(cptr, cptr, &me, "Ping timeout"); } continue; } else if (IsRegistered(cptr) && (cptr->flags & FLAGS_PINGSENT) == 0) { /* * if we havent PINGed the connection and we havent * heard from it in a while, PING it to make sure * it is still alive. */ cptr->flags |= FLAGS_PINGSENT; /* not nice but does the job */ cptr->lasttime = currenttime - ping; sendto_one(cptr, "PING :%s", me.name); } ping_timeout: timeout = cptr->lasttime + ping; while (timeout <= currenttime) timeout += ping; if (timeout < oldest || !oldest) oldest = timeout; } #ifdef TIMEDKLINES if (currenttime - lkill > 60) lkill = currenttime; #endif if (!oldest || oldest < currenttime) oldest = currenttime + PINGFREQUENCY; if (oldest < currenttime + 30) oldest += 30; Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return (oldest); }
/** Read a 'packet' of data from a connection and process it. Read in * 8k chunks to give a better performance rating (for server * connections). Do some tricky stuff for client connections to make * sure they don't do any flooding >:-) -avalon * @param cptr Client from which to read data. * @param socket_ready If non-zero, more data can be read from the client's socket. * @return Positive number on success, zero on connection-fatal failure, negative * if user is killed. */ static int read_packet(struct Client *cptr, int socket_ready) { unsigned int dolen = 0; unsigned int length = 0; if (socket_ready && !(IsUser(cptr) && !IsOper(cptr) && DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) { switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) { case IO_SUCCESS: if (length) { cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); ClrFlag(cptr, FLAG_NONL); if (cli_lasttime(cptr) > cli_since(cptr)) cli_since(cptr) = cli_lasttime(cptr); } break; case IO_BLOCKED: break; case IO_FAILURE: cli_error(cptr) = errno; /* SetFlag(cptr, FLAG_DEADSOCKET); */ return 0; } } /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ if (length > 0 && IsServer(cptr)) return server_dopacket(cptr, readbuf, length); else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr))) return connect_dopacket(cptr, readbuf, length); else { /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its * turn comes around. */ if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); if (IsUser(cptr)) { if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD) && !IsOper(cptr)) return exit_client(cptr, cptr, &me, "Excess Flood"); } while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10)) { dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* * Devious looking...whats it do ? well..if a client * sends a *long* message without any CR or LF, then * dbuf_getmsg fails and we pull it out using this * loop which just gets the next 512 bytes and then * deletes the rest of the buffer contents. * -avalon */ if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { /* More than 512 bytes in the line - drop the input and yell * at the client. */ DBufClear(&(cli_recvQ(cptr))); send_reply(cptr, ERR_INPUTTOOLONG); } } else if (client_dopacket(cptr, dolen) == CPTR_KILLED) return CPTR_KILLED; /* * If it has become registered as a Server * then skip the per-message parsing below. */ if (IsHandshake(cptr) || IsServer(cptr)) { while (-1) { dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf)); if (dolen <= 0) return 1; else if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else DBufClear(&(cli_recvQ(cptr))); } else if ((IsServer(cptr) && server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) || (!IsServer(cptr) && connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED)) return CPTR_KILLED; } } } /* If there's still data to process, wait 2 seconds first */ if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && !t_onqueue(&(cli_proc(cptr)))) { Debug((DEBUG_LIST, "Adding client process timer for %C", cptr)); cli_freeflag(cptr) |= FREEFLAG_TIMER; timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr), TT_RELATIVE, 2); } } return 1; }