/* * m_pass() - Added Sat, 4 March 1989 * * * m_pass - PASS message handler * parv[0] = sender prefix * parv[1] = password * parv[2] = optional extra version information */ int m_pass(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { char* password = parc > 1 ? parv[1] : NULL; if (EmptyString(password)) { sendto_one(cptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PASS"); return 0; } if (!MyConnect(sptr) || (!IsUnknown(cptr) && !IsHandshake(cptr))) { sendto_one(cptr, form_str(ERR_ALREADYREGISTRED), me.name, parv[0]); return 0; } strncpy_irc(cptr->passwd, password, PASSWDLEN); if (parc > 2) { /* * It looks to me as if orabidoo wanted to have more * than one set of option strings possible here... * i.e. ":AABBTS" as long as TS was the last two chars * however, as we are now using CAPAB, I think we can * safely assume if there is a ":TS" then its a TS server * -Dianora */ if ((0 == irccmp(parv[2], "TS")) && (cptr->tsinfo == 0)) cptr->tsinfo = TS_DOESTS; } return 0; }
/* * 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; }
/* * 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); } }
/** 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; }
/* * Attempt to send a sequence of bytes to the connection. * Returns * * < 0 Some fatal error occurred, (but not EWOULDBLOCK). * This return is a request to close the socket and * clean up the link. * * >= 0 No real error occurred, returns the number of * bytes actually transferred. EWOULDBLOCK and other * possibly similar conditions should be mapped to * zero return. Upper level routine will have to * decide what to do with those unwritten bytes... * * *NOTE* alarm calls have been preserved, so this should * work equally well whether blocking or non-blocking * mode is used... */ int deliver_it(aClient *cptr, char *str, int len) { int retval; aClient *acpt = cptr->acpt; #ifdef DEBUGMODE writecalls++; #endif if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) && !IsUnknown(cptr))) { str[len]='\0'; sendto_ops("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr)?"DEAD":"", str); return -1; } retval = socket_write(cptr->sock, str, len); /* * Convert WOULDBLOCK to a return of "0 bytes moved". This * should occur only if socket was non-blocking. Note, that * all is Ok, if the 'write' just returns '0' instead of an * error and errno=EWOULDBLOCK. */ #ifdef DEBUGMODE if (retval < 0) { writeb[0]++; Debug((DEBUG_ERROR,"write error (%s) to %s", strerror(errno), cptr->name)); } else if (retval == 0) writeb[1]++; else if (retval < 16) writeb[2]++; else if (retval < 32) writeb[3]++; else if (retval < 64) writeb[4]++; else if (retval < 128) writeb[5]++; else if (retval < 256) writeb[6]++; else if (retval < 512) writeb[7]++; else if (retval < 1024) writeb[8]++; else writeb[9]++; #endif if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
/* 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); }
/* * 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; }
/** 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; } } }
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; }
/* * m_server * parv[0] = sender prefix * parv[1] = servername * parv[2] = serverinfo/hopcount * parv[3] = serverinfo */ int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int i; char info[REALLEN + 1], *host; aClient *acptr, *bcptr; aConnect *aconn; int hop; char nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */ info[0] = '\0'; if (parc < 2 || *parv[1] == '\0') { sendto_one(cptr, "ERROR :No servername"); return 0; } hop = 0; host = parv[1]; if (parc > 3 && atoi(parv[2])) { hop = atoi(parv[2]); strncpyzt(info, parv[3], REALLEN + 1); } else if (parc > 2) { strncpyzt(info, parv[2], REALLEN + 1); if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2))) { strcat(info, " "); strncat(info, parv[3], REALLEN - i - 2); info[REALLEN] = '\0'; } } /* * July 5, 1997 * Rewritten to throw away server cruft from users, * combined the hostname validity test with cleanup of host name, * so a cleaned up hostname can be returned as an error if * necessary. - Dianora */ /* yes, the if(strlen) below is really needed!! */ if (strlen(host) > HOSTLEN) host[HOSTLEN] = '\0'; if (IsPerson(cptr)) { /* A local link that has been identified as a USER tries * something fishy... ;-) */ sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND), me.name, parv[0], "SERVER"); return 0; } else /* hostile servername check */ { /* * Lets check for bogus names and clean them up we don't bother * cleaning up ones from users, becasuse we will never see them * any more - Dianora */ int bogus_server = 0; int found_dot = 0; char clean_host[(2 * HOSTLEN) + 1]; char *s; char *d; int n; s = host; d = clean_host; n = (2 * HOSTLEN) - 2; while (*s && n > 0) { if ((unsigned char) *s < (unsigned char) ' ') /* Is it a control character? */ { bogus_server = 1; *d++ = '^'; *d++ = (char) ((unsigned char) *s + 0x40); /* turn it into a printable */ n -= 2; } else if ((unsigned char) *s > (unsigned char) '~') { bogus_server = 1; *d++ = '.'; n--; } else { if (*s == '.') found_dot = 1; *d++ = *s; n--; } s++; } *d = '\0'; if ((!found_dot) || bogus_server) { sendto_one(sptr, "ERROR :Bogus server name (%s)", clean_host); return exit_client(cptr, cptr, cptr, "Bogus server name"); } } /* new connection */ if (IsUnknown(cptr) || IsHandshake(cptr)) { strncpyzt(cptr->name, host, sizeof(cptr->name)); strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN + 1); cptr->hopcount = hop; switch (check_server_init(cptr)) { case 0: return m_server_estab(cptr); case 1: sendto_ops("Access check for %s in progress", get_client_name(cptr, HIDEME)); return 1; default: ircstp->is_ref++; sendto_ops_lev(ADMIN_LEV, "Link %s dropped, no Connect block", get_client_name(cptr, TRUE)); return exit_client(cptr, cptr, cptr, "No Connect block"); } } /* already linked server */ if (!IsServer(cptr)) return 0; if ((acptr = find_name(host, NULL))) { /* * * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon */ bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr : acptr->from; sendto_one(bcptr, "ERROR :Server %s already exists", host); if (bcptr == cptr) { /* Don't complain for servers that are juped */ /* (don't complain if the server that already exists is U: lined, unless I actually have a .conf U: line for it */ if(!IsULine(acptr) || find_aUserver(acptr->name)) { sendto_gnotice("from %s: Link %s cancelled, server %s already " "exists", me.name, get_client_name(bcptr, HIDEME), host); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, " "server %s already exists", me.name, get_client_name(bcptr, HIDEME), host); } return exit_client(bcptr, bcptr, &me, "Server Exists"); } /* inform all those who care (set +n) -epi */ strcpy(nbuf, get_client_name(bcptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced " "by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s " "reintroduced by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); exit_client(bcptr, bcptr, &me, "Server Exists"); } /* * The following if statement would be nice to remove since user * nicks never have '.' in them and servers must always have '.' in * them. There should never be a server/nick name collision, but it * is possible a capricious server admin could deliberately do * something strange. * * -Dianora */ if ((acptr = find_client(host, NULL)) && acptr != cptr) { /* * * Server trying to use the same name as a person. Would * cause a fair bit of confusion. Enough to make it hellish for * a while and servers to send stuff to the wrong place. */ sendto_one(cptr, "ERROR :Nickname %s already exists!", host); strcpy(nbuf, get_client_name(cptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, servername/nick collision", me.name, nbuf); sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, " "servername/nick collision", me.name, nbuf); return exit_client(cptr, cptr, cptr, "Nick as Server"); } if (IsServer(cptr)) { /* * * Server is informing about a new server behind this link. * Create REMOTE server structure, add it to list and propagate * word to my other server links... */ if (parc == 1 || info[0] == '\0') { sendto_one(cptr, "ERROR :No server info specified for %s", host); return 0; } /* * * See if the newly found server is behind a guaranteed leaf * (L-line). If so, close the link. * * Depreciated. Kinda redundant with Hlines. -epi */ if (!(cptr->serv->aconn->flags & CONN_HUB)) { aconn = cptr->serv->aconn; sendto_gnotice("from %s: Non-Hub link %s introduced %s", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced " "%s", me.name, get_client_name(cptr, HIDEME), host); sendto_one(cptr, "ERROR :You're not a hub (introducing %s)", host); return exit_client(cptr, cptr, cptr, "Too many servers"); } acptr = make_client(cptr, sptr); make_server(acptr); acptr->hopcount = hop; strncpyzt(acptr->name, host, sizeof(acptr->name)); strncpyzt(acptr->info, info, REALLEN + 1); acptr->serv->up = find_or_add(parv[0]); fakelinkserver_update(acptr->name, acptr->info); SetServer(acptr); /* * if this server is behind a U-lined server, make it U-lined as * well. - lucas */ if (IsULine(sptr) || find_aUserver(acptr->name)) { acptr->flags |= FLAGS_ULINE; sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s", cptr->name, acptr->name); } Count.server++; add_client_to_list(acptr); add_to_client_hash_table(acptr->name, acptr); /* * Old sendto_serv_but_one() call removed because we now need * to send different names to different servers (domain name matching) */ for (i = 0; i <= highest_fd; i++) { if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr || IsMe(bcptr)) continue; if (!(aconn = bcptr->serv->aconn)) { sendto_gnotice("from %s: Lost Connect block for %s on %s." " Closing", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for" " %s on %s. Closing", me.name, get_client_name(cptr, HIDEME), host); return exit_client(cptr, cptr, cptr, "Lost Connect block"); } if (match(my_name_for_link(me.name, aconn), acptr->name) == 0) continue; sendto_one(bcptr, ":%s SERVER %s %d :%s", parv[0], acptr->name, hop + 1, acptr->info); } return 0; } return 0; }
/** Check access for a server given its name (passed in cptr struct). * Must check for all C/N lines which have a name which matches the * name given and a host which matches. A host alias which is the * same as the server name is also acceptable in the host field of a * C/N line. * @param cptr Peer server to check. * @return 0 if accepted, -1 if access denied. */ int conf_check_server(struct Client *cptr) { struct ConfItem* c_conf = NULL; struct SLink* lp; Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", cli_name(cptr), cli_sockhost(cptr))); if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) { Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr))); return -1; } lp = cli_confs(cptr); /* * We initiated this connection so the client should have a C and N * line already attached after passing through the connect_server() * function earlier. */ if (IsConnecting(cptr) || IsHandshake(cptr)) { c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER); if (!c_conf) { sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost Connect block for %s", cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } /* Try finding the Connect block by DNS name and IP next. */ if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER))) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * Attach by IP# only if all other checks have failed. * It is quite possible to get here with the strange things that can * happen when using DNS in the way the irc server does. -avalon */ if (!c_conf) c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * detach all conf lines that got attached by attach_confs() */ det_confs_butmask(cptr, 0); /* * if no Connect block, then deny access */ if (!c_conf) { Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]", cli_name(cptr), cli_username(cptr), cli_sockhost(cptr))); return -1; } /* * attach the Connect block to the client structure for later use. */ attach_conf(cptr, c_conf); if (!irc_in_addr_valid(&c_conf->address.addr)) memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr)); Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cli_name(cptr), cli_sockhost(cptr))); return 0; }
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); }
/** Handle a connection that has sent a valid PASS and SERVER. * @param cptr New peer server. * @param aconf Connect block for \a cptr. * @return Zero. */ int server_estab(struct Client *cptr, struct ConfItem *aconf) { struct Client* acptr = 0; const char* inpath; int i; assert(0 != cptr); assert(0 != cli_local(cptr)); inpath = cli_name(cptr); if (IsUnknown(cptr)) { if (aconf->passwd[0]) sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd); /* * Pass my info to the new server */ sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s", cli_name(&me), cli_serv(&me)->timestamp, cli_serv(cptr)->timestamp, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", *(cli_info(&me)) ? cli_info(&me) : "IRCers United"); } det_confs_butmask(cptr, CONF_SERVER); if (!IsHandshake(cptr)) hAddClient(cptr); SetServer(cptr); cli_handler(cptr) = SERVER_HANDLER; Count_unknownbecomesserver(UserStats); SetBurst(cptr); /* nextping = CurrentTime; */ /* * NOTE: check for acptr->user == cptr->serv->user is necessary to insure * that we got the same one... bleah */ if (cli_serv(cptr)->user && *(cli_serv(cptr))->by && (acptr = findNUser(cli_serv(cptr)->by))) { if (cli_user(acptr) == cli_serv(cptr)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s established.", acptr, inpath); } else { /* * if not the same client, set by to empty string */ acptr = 0; *(cli_serv(cptr))->by = '\0'; } } sendto_opmask(acptr, SNO_OLDSNO, "Link with %s established.", inpath); cli_serv(cptr)->up = &me; cli_serv(cptr)->updown = add_dlink(&(cli_serv(&me))->down, cptr); sendto_opmask(0, SNO_NETWORK, "Net junction: %s %s", cli_name(&me), cli_name(cptr)); SetJunction(cptr); /* * Old sendto_serv_but_one() call removed because we now * need to send different names to different servers * (domain name matching) Send new server to other servers. */ for (i = 0; i <= HighestFd; i++) { if (!(acptr = LocalClientArray[i]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) continue; if (!match(cli_name(&me), cli_name(cptr))) continue; sendcmdto_one(&me, CMD_SERVER, acptr, "%s 2 0 %Tu J%02u %s%s +%s%s%s :%s", cli_name(cptr), cli_serv(cptr)->timestamp, Protocol(cptr), NumServCap(cptr), IsHub(cptr) ? "h" : "", IsService(cptr) ? "s" : "", IsIPv6(cptr) ? "6" : "", cli_info(cptr)); } /* Send these as early as possible so that glined users/juped servers can * be removed from the network while the remote server is still chewing * our burst. */ gline_burst(cptr); jupe_burst(cptr); /* * Pass on my client information to the new server * * First, pass only servers (idea is that if the link gets * canceled because the server was already there, * there are no NICK's to be canceled...). Of course, * if cancellation occurs, all this info is sent anyway, * and I guess the link dies when a read is attempted...? --msa * * Note: Link cancellation to occur at this point means * that at least two servers from my fragment are building * up connection this other fragment at the same time, it's * a race condition, not the normal way of operation... */ for (acptr = &me; acptr; acptr = cli_prev(acptr)) { /* acptr->from == acptr for acptr == cptr */ if (cli_from(acptr) == cptr) continue; if (IsServer(acptr)) { const char* protocol_str; if (Protocol(acptr) > 9) protocol_str = IsBurst(acptr) ? "J" : "P"; else protocol_str = IsBurst(acptr) ? "J0" : "P0"; if (0 == match(cli_name(&me), cli_name(acptr))) continue; sendcmdto_one(cli_serv(acptr)->up, CMD_SERVER, cptr, "%s %d 0 %Tu %s%u %s%s +%s%s%s :%s", cli_name(acptr), cli_hopcount(acptr) + 1, cli_serv(acptr)->timestamp, protocol_str, Protocol(acptr), NumServCap(acptr), IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "", cli_info(acptr)); } } for (acptr = &me; acptr; acptr = cli_prev(acptr)) { /* acptr->from == acptr for acptr == cptr */ if (cli_from(acptr) == cptr) continue; if (IsUser(acptr)) { char xxx_buf[25]; char *s = umode_str(acptr); sendcmdto_one(cli_user(acptr)->server, CMD_NICK, cptr, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", cli_name(acptr), cli_hopcount(acptr) + 1, cli_lastnick(acptr), cli_user(acptr)->username, cli_user(acptr)->realhost, *s ? "+" : "", s, *s ? " " : "", iptobase64(xxx_buf, &cli_ip(acptr), sizeof(xxx_buf), IsIPv6(cptr)), NumNick(acptr), cli_info(acptr)); } } /* * Last, send the BURST. * (Or for 2.9 servers: pass all channels plus statuses) */ { struct Channel *chptr; for (chptr = GlobalChannelList; chptr; chptr = chptr->next) send_channel_modes(cptr, chptr); } sendcmdto_one(&me, CMD_END_OF_BURST, cptr, ""); return 0; }
/** Attempt to send a sequence of bytes to the connection. * As a side effect, updates \a cptr's FLAG_BLOCKED setting * and sendB/sendK fields. * @param cptr Client that should receive data. * @param buf Message buffer to send to client. * @return Negative on connection-fatal error; otherwise * number of bytes sent. */ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf) { unsigned int bytes_written = 0; unsigned int bytes_count = 0; assert(0 != cptr); #if defined(USE_SSL) switch (client_sendv(cptr, buf, &bytes_count, &bytes_written)) { #else switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) { #endif case IO_SUCCESS: ClrFlag(cptr, FLAG_BLOCKED); cli_sendB(cptr) += bytes_written; cli_sendB(&me) += bytes_written; /* A partial write implies that future writes will block. */ if (bytes_written < bytes_count) SetFlag(cptr, FLAG_BLOCKED); break; case IO_BLOCKED: SetFlag(cptr, FLAG_BLOCKED); break; case IO_FAILURE: cli_error(cptr) = errno; SetFlag(cptr, FLAG_DEADSOCKET); break; } return bytes_written; } /** 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; #if defined(USE_SSL) char *sslfp; int r; #endif 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(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(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr)); return 0; } #if defined(USE_SSL) if (aconf->flags & CONF_SSL) { r = ssl_connect(&(cli_socket(cptr))); if (r == -1) { sendto_opmask(0, SNO_OLDSNO, "Connection failed to %s: SSL error", cli_name(cptr)); return 0; } else if (r == 0) return 1; sslfp = ssl_get_fingerprint(cli_socket(cptr).s_ssl); if (sslfp) ircd_strncpy(cli_sslclifp(cptr), sslfp, BUFSIZE+1); SetSSL(cptr); } #endif 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); /* TODO: NEGOCIACION envia_config_req(cptr); */ sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s", cli_name(&me), cli_serv(&me)->timestamp, newts, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me)); #if defined(DDB) ddb_burst(cptr); #endif return (IsDead(cptr)) ? 0 : 1; } /** Close the physical connection. Side effects: MyConnect(cptr) * becomes false and cptr->from becomes NULL. * @param cptr Client to disconnect. */ void close_connection(struct Client *cptr) { struct ConfItem* aconf; if (IsServer(cptr)) { ServerStats->is_sv++; ServerStats->is_sbs += cli_sendB(cptr); ServerStats->is_sbr += cli_receiveB(cptr); ServerStats->is_sti += CurrentTime - cli_firsttime(cptr); /* * If the connection has been up for a long amount of time, schedule * a 'quick' reconnect, else reset the next-connect cycle. */ if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) { /* * Reschedule a faster reconnect, if this was a automatically * connected configuration entry. (Note that if we have had * a rehash in between, the status has been changed to * CONF_ILLEGAL). But only do this if it was a "good" link. */ aconf->hold = CurrentTime; aconf->hold += ((aconf->hold - cli_since(cptr) > feature_int(FEAT_HANGONGOODLINK)) ? feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf)); /* if (nextconnect > aconf->hold) */ /* nextconnect = aconf->hold; */ } } else if (IsUser(cptr)) { ServerStats->is_cl++; ServerStats->is_cbs += cli_sendB(cptr); ServerStats->is_cbr += cli_receiveB(cptr); ServerStats->is_cti += CurrentTime - cli_firsttime(cptr); } else ServerStats->is_ni++; #if defined(USE_ZLIB) /* * Siempre es una conexion nuestra */ if (cli_connect(cptr)->zlib_negociation & ZLIB_IN) { inflateEnd(cli_connect(cptr)->comp_in); MyFree(cli_connect(cptr)->comp_in); } if (cli_connect(cptr)->zlib_negociation & ZLIB_OUT) { deflateEnd(cli_connect(cptr)->comp_out); MyFree(cli_connect(cptr)->comp_out); } #endif if (-1 < cli_fd(cptr)) { flush_connections(cptr); LocalClientArray[cli_fd(cptr)] = 0; close(cli_fd(cptr)); socket_del(&(cli_socket(cptr))); /* queue a socket delete */ cli_fd(cptr) = -1; cli_freeflag(cptr) &= ~FREEFLAG_SOCKET; } SetFlag(cptr, FLAG_DEADSOCKET); MsgQClear(&(cli_sendQ(cptr))); client_drop_sendq(cli_connect(cptr)); DBufClear(&(cli_recvQ(cptr))); memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); set_snomask(cptr, 0, SNO_SET); det_confs_butmask(cptr, 0); if (cli_listener(cptr)) { release_listener(cli_listener(cptr)); cli_listener(cptr) = 0; } for ( ; HighestFd > 0; --HighestFd) { if (LocalClientArray[HighestFd]) break; } } /** Close all unregistered connections. * @param source Oper who requested the close. * @return Number of closed connections. */ int net_close_unregistered_connections(struct Client* source) { int i; struct Client* cptr; int count = 0; assert(0 != source); for (i = HighestFd; i > 0; --i) { if ((cptr = LocalClientArray[i]) && !IsRegistered(cptr)) { send_reply(source, RPL_CLOSING, get_client_name(source, HIDE_IP)); exit_client(source, cptr, &me, "Oper Closing"); ++count; } } return count; }
/* * 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: */ } }
/* ** 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; }
/** 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); }
/* * 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); }
/** 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; }
/* ** 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; }
/** 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)); }
/* ** deliver_it ** Attempt to send a sequence of bytes to the connection. ** Returns ** ** < 0 Some fatal error occurred, (but not EWOULDBLOCK). ** This return is a request to close the socket and ** clean up the link. ** ** >= 0 No real error occurred, returns the number of ** bytes actually transferred. EWOULDBLOCK and other ** possibly similar conditions should be mapped to ** zero return. Upper level routine will have to ** decide what to do with those unwritten bytes... ** ** *NOTE* alarm calls have been preserved, so this should ** work equally well whether blocking or non-blocking ** mode is used... ** ** *NOTE* I nuked 'em. At the load of current ircd servers ** you can't run with stuff that blocks. And we don't. */ int deliver_it(aClient *cptr, char *str, int len) { int retval; aClient *acpt = cptr->listener; #ifdef DEBUGMODE writecalls++; #endif #ifdef VMS retval = netwrite(cptr->fd, str, len); #else if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) #ifdef USE_SSL && !IsSSLHandshake(cptr) #endif && !IsUnknown(cptr))) { str[len] = '\0'; sendto_ops ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str); return -1; } #ifdef USE_SSL if (cptr->flags & FLAGS_SSL) retval = ircd_SSL_write(cptr, str, len); else #endif retval = send(cptr->fd, str, len, 0); /* ** Convert WOULDBLOCK to a return of "0 bytes moved". This ** should occur only if socket was non-blocking. Note, that ** all is Ok, if the 'write' just returns '0' instead of an ** error and errno=EWOULDBLOCK. ** ** ...now, would this work on VMS too? --msa */ # ifndef _WIN32 if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS)) # else if (retval < 0 && (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAENOBUFS)) # endif { retval = 0; SetBlocked(cptr); } else if (retval > 0) { ClearBlocked(cptr); } #endif #ifdef DEBUGMODE if (retval < 0) { writeb[0]++; Debug((DEBUG_ERROR, "write error (%s) to %s", STRERROR(ERRNO), cptr->name)); } else if (retval == 0) writeb[1]++; else if (retval < 16) writeb[2]++; else if (retval < 32) writeb[3]++; else if (retval < 64) writeb[4]++; else if (retval < 128) writeb[5]++; else if (retval < 256) writeb[6]++; else if (retval < 512) writeb[7]++; else if (retval < 1024) writeb[8]++; else writeb[9]++; #endif if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
/* 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); }
/* ** deliver_it ** Attempt to send a sequence of bytes to the connection. ** Returns ** ** < 0 Some fatal error occurred, (but not EWOULDBLOCK). ** This return is a request to close the socket and ** clean up the link. ** ** >= 0 No real error occurred, returns the number of ** bytes actually transferred. EWOULDBLOCK and other ** possibly similar conditions should be mapped to ** zero return. Upper level routine will have to ** decide what to do with those unwritten bytes... ** ** *NOTE* alarm calls have been preserved, so this should ** work equally well whether blocking or non-blocking ** mode is used... ** ** *NOTE* I nuked 'em. At the load of current ircd servers ** you can't run with stuff that blocks. And we don't. */ int deliver_it(aClient *cptr, char *str, int len) { int retval; if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) #ifdef USE_SSL && !IsSSLHandshake(cptr) #endif && !IsUnknown(cptr))) { str[len] = '\0'; sendto_ops ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str); return -1; } #ifdef USE_SSL if (IsSSL(cptr) && cptr->ssl != NULL) { retval = SSL_write(cptr->ssl, str, len); if (retval < 0) { switch (SSL_get_error(cptr->ssl, retval)) { case SSL_ERROR_WANT_READ: /* retry later */ return 0; case SSL_ERROR_WANT_WRITE: SET_ERRNO(P_EWOULDBLOCK); break; case SSL_ERROR_SYSCALL: break; case SSL_ERROR_SSL: if (ERRNO == P_EAGAIN) break; default: return 0; } } } else #endif retval = send(cptr->fd, str, len, 0); /* ** Convert WOULDBLOCK to a return of "0 bytes moved". This ** should occur only if socket was non-blocking. Note, that ** all is Ok, if the 'write' just returns '0' instead of an ** error and errno=EWOULDBLOCK. ** ** ...now, would this work on VMS too? --msa */ if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS)) retval = 0; if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
/** * 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; }
/** Handle a SERVER message from an unregistered connection. * * \a parv has the following elements: * \li \a parv[1] is the server name * \li \a parv[2] is the hop count to the server * \li \a parv[3] is the start timestamp for the server * \li \a parv[4] is the link timestamp * \li \a parv[5] is the protocol version (P10 or J10) * \li \a parv[6] is the numnick mask for the server * \li \a parv[7] is a string of flags like +hs to mark hubs and services * \li \a parv[\a parc - 1] is the server description * * 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 mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* host; struct ConfItem* aconf; struct Jupe* ajupe; unsigned int hop; int ret; unsigned short prot; time_t start_timestamp; time_t timestamp; time_t recv_time; time_t ghost; if (IsUserPort(cptr)) return exit_client_msg(cptr, cptr, &me, "Cannot connect a server to a user port"); if (parc < 8) { need_more_params(sptr, "SERVER"); return exit_client(cptr, cptr, &me, "Need more parameters"); } host = clean_servername(parv[1]); if (!host) { sendto_opmask(0, SNO_OLDSNO, "Bogus server name (%s) from %s", host, cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); } if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe)) return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe)); /* check connection rules */ if (0 != conf_eval_crule(host, CRULE_ALL)) { ServerStats->is_ref++; sendto_opmask(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr)); return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); } log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host, cli_sockhost(cptr), cli_sock_ip(cptr)); /* * Detect protocol */ hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); prot = parse_protocol(parv[5]); if (!prot) return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, "Incompatible protocol: %s", parv[5]); Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS) return exit_client_msg(cptr, sptr, &me, "Bogus timestamps (%s %s)", parv[3], parv[4]); /* If the server had a different name before, change it. */ if (!EmptyString(cli_name(cptr)) && (IsUnknown(cptr) || IsHandshake(cptr)) && 0 != ircd_strcmp(cli_name(cptr), host)) hChangeClient(cptr, host); ircd_strncpy(cli_name(cptr), host, HOSTLEN); ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN); cli_hopcount(cptr) = hop; if (conf_check_server(cptr)) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Received unauthorized connection from %s", cli_name(cptr)); log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized " "connection from %C [%s]", cptr, ircd_ntoa(&cli_ip(cptr))); return exit_client(cptr, cptr, &me, "No Connect block"); } host = cli_name(cptr); update_load(); if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied. No conf line for server %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Access denied. No conf line for server %s", cli_name(cptr)); } #if defined(USE_SSL) if (!verify_sslclifp(cptr, aconf)) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied (SSL fingerprint mismatch) %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "No Access (SSL fingerprint mismatch) %s", cli_name(cptr)); } #endif if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "No Access (passwd mismatch) %s", cli_name(cptr)); } memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1); if (ret != 1) return ret; make_server(cptr); cli_serv(cptr)->timestamp = timestamp; cli_serv(cptr)->prot = prot; cli_serv(cptr)->ghost = ghost; memset(cli_privs(cptr), 255, sizeof(struct Privs)); ClrPriv(cptr, PRIV_SET); SetServerYXX(cptr, cptr, parv[6]); update_uworld_flags(cptr); if (*parv[7] == '+') set_server_flags(cptr, parv[7] + 1); recv_time = TStime(); check_start_timestamp(cptr, timestamp, start_timestamp, recv_time); ret = server_estab(cptr, aconf); if (feature_bool(FEAT_RELIABLE_CLOCK) && abs(cli_serv(cptr)->timestamp - recv_time) > 30) { sendto_opmask(0, SNO_OLDSNO, "Connected to a net with a " "timestamp-clock difference of %Td seconds! " "Used SETTIME to correct this.", timestamp - recv_time); sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(), cli_name(&me)); } return ret; }
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) { #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); }
/* ** DoNumeric (replacement for the old do_numeric) ** ** parc number of arguments ('sender' counted as one!) ** parv[0] pointer to 'sender' (may point to empty string) (not used) ** parv[1]..parv[parc-1] ** pointers to additional parameters, this is a NULL ** terminated list (parv[parc] == NULL). ** ** *WARNING* ** Numerics are mostly error reports. If there is something ** wrong with the message, just *DROP* it! Don't even think of ** sending back a neat error message -- big danger of creating ** a ping pong error message... */ int do_numeric(int numeric, aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; aChannel *chptr; char *nick, *p; int i; /* Is this an outgoing connect, and we get a numeric 451 (not registered) back for the * magic command __PANGPANG__ and we did not send a SERVER message yet? * Then this means we are dealing with an Unreal server <3.2.9 and we should send the * SERVER command right now. */ if (!IsServer(sptr) && !IsPerson(sptr) && (numeric == 451) && (parc > 2) && strstr(parv[1], "__PANGPANG__") && IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr)) { send_server_message(sptr); return 0; } if (parc < 1 || !IsServer(sptr)) return 0; /* Remap low number numerics. */ if (numeric < 100) numeric += 100; /* ** Prepare the parameter portion of the message into 'buffer'. ** (Because the buffer is twice as large as the message buffer ** for the socket, no overflow can occur here... ...on current ** assumptions--bets are off, if these are changed --msa) ** Note: if buffer is non-empty, it will begin with SPACE. */ buffer[0] = '\0'; if (parc > 2) { /* * For strlcat nazis, please read above */ for (i = 2; i < (parc - 1); i++) { (void)strcat(buffer, " "); (void)strcat(buffer, parv[i]); } (void)strcat(buffer, " :"); (void)strcat(buffer, parv[parc - 1]); } else sendto_realops("do_numeric( %i, %s, %s, %i, { %s, %s } )!", numeric, cptr->name, sptr->name, parc, parv[0], parv[1] ? parv[1] : "<null>"); for (; (nick = strtoken(&p, parv[1], ",")); parv[1] = NULL) { if ((acptr = find_client(nick, (aClient *)NULL))) { /* ** Drop to bit bucket if for me... ** ...one might consider sendto_ops ** here... --msa ** And so it was done. -avalon ** And regretted. Dont do it that way. Make sure ** it goes only to non-servers. -avalon ** Check added to make sure servers don't try to loop ** with numerics which can happen with nick collisions. ** - Avalon */ if (!IsMe(acptr) && IsPerson(acptr)) { /* Added for .U3.2. drop remote 'You are not on ** that channel', we should be synced anyway, ** and this is an annoying message with TSpre7 ** still on the net; would result in numeric 442 for ** every KICK... Can be removed when TSpre7 is gone. ** --Run if (numeric==ERR_NOTONCHANNEL) return 0; */ sendto_prefix_one(acptr, sptr, ":%s %d %s%s", parv[0], numeric, nick, buffer); } else if (IsServer(acptr) && acptr->from != cptr) sendto_prefix_one(acptr, sptr, ":%s %d %s%s", parv[0], numeric, nick, buffer); } else if ((acptr = find_server_quick(nick))) { if (!IsMe(acptr) && acptr->from != cptr) sendto_prefix_one(acptr, sptr, ":%s %d %s%s", parv[0], numeric, nick, buffer); } else if ((chptr = find_channel(nick, (aChannel *)NULL))) sendto_channel_butone(cptr, sptr, chptr, ":%s %d %s%s", parv[0], numeric, chptr->chname, buffer); } return 0; }