/** Create a new Z-line and add it to global lists. * \a ipmask must be an IP mask to create an IP-based ban. * * @param[in] cptr Client that sent us the Z-line. * @param[in] sptr Client that originated the Z-line. * @param[in] ipmask Text mask for the Z-line. * @param[in] reason Reason for Z-line. * @param[in] expire Expiration time of Z-line. * @param[in] lastmod Last modification time of Z-line. * @param[in] lifetime Lifetime of Z-line. * @param[in] flags Bitwise combination of ZLINE_* flags. * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. */ int zline_add(struct Client *cptr, struct Client *sptr, char *ipmask, char *reason, time_t expire, time_t lastmod, time_t lifetime, unsigned int flags) { struct Zline *azline; char imask[HOSTLEN + 2]; char *mask; int tmp; assert(0 != ipmask); assert(0 != reason); assert(((flags & (ZLINE_GLOBAL | ZLINE_LOCAL)) == ZLINE_GLOBAL) || ((flags & (ZLINE_GLOBAL | ZLINE_LOCAL)) == ZLINE_LOCAL)); Debug((DEBUG_DEBUG, "zline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu " "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), ipmask, reason, expire, lastmod, lifetime, flags)); mask = ipmask; if (sizeof(imask) < ircd_snprintf(0, imask, sizeof(imask), "%s", mask)) return send_reply(sptr, ERR_LONGMASK); else if (MyUser(sptr) || (IsUser(sptr) && flags & ZLINE_LOCAL)) { switch (zline_checkmask(mask)) { case CHECK_OVERRIDABLE: /* oper overrided restriction */ if (flags & ZLINE_OPERFORCE) break; /*FALLTHROUGH*/ case CHECK_REJECTED: return send_reply(sptr, ERR_MASKTOOWIDE, imask); break; } if ((tmp = count_users(imask, flags)) >= feature_int(FEAT_ZLINEMAXUSERCOUNT) && !(flags & ZLINE_OPERFORCE)) return send_reply(sptr, ERR_TOOMANYUSERS, tmp); } if (!check_if_ipmask(ipmask)) return send_reply(sptr, ERR_INVALIDMASK); /* * You cannot set a negative (or zero) expire time, nor can you set an * expiration time for greater than ZLINE_MAX_EXPIRE. */ if (!(flags & ZLINE_FORCE) && (expire <= TStime() || expire > TStime() + ZLINE_MAX_EXPIRE)) { if (!IsServer(sptr) && MyConnect(sptr)) send_reply(sptr, ERR_BADEXPIRE, expire); return 0; } else if (expire <= TStime()) { /* This expired Z-line was forced to be added, so mark it inactive. */ flags &= ~ZLINE_ACTIVE; } if (!lifetime) /* no lifetime set, use expiration time */ lifetime = expire; /* lifetime is already an absolute timestamp */ /* Inform ops... */ sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE : SNO_AUTO, "%s adding %s%s ZLINE for %s, expiring at " "%Tu: %s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), (flags & ZLINE_ACTIVE) ? "" : "deactivated ", (flags & ZLINE_LOCAL) ? "local" : "global", mask, expire, reason); /* and log it */ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C adding %s ZLINE for %s, expiring at %Tu: %s", sptr, flags & ZLINE_LOCAL ? "local" : "global", mask, expire, reason); /* make the zline */ azline = make_zline(mask, reason, expire, lastmod, lifetime, flags); /* since we've disabled overlapped Z-line checking, azline should * never be NULL... */ assert(azline); zline_propagate(cptr, sptr, azline); return do_zline(cptr, sptr, azline); /* knock off users if necessary */ }
static void vsendto_prefix_one(struct Client *to, struct Client *from, char *pattern, va_list vlorig) { va_list vl; va_copy(vl,vlorig); if (to && from && MyUser(to) && IsUser(from)) { static char sender[HOSTLEN + NICKLEN + USERLEN + 5]; char *par; int flag = 0; Reg3 anUser *user = from->cli_user; par = va_arg(vl, char *); strcpy(sender, from->name); #if defined(ESNET_NEG) if (user && !(to->cli_connect->negociacion & USER_TOK)) #else if (user) #endif { if (user->username) { strcat(sender, "!"); strcat(sender, user->username); } if (user->host && !MyConnect(from)) { strcat(sender, "@"); #if defined(BDD_VIP) strcat(sender, get_visiblehost(from, NULL)); #else strcat(sender, user->host); #endif flag = 1; } } /* * Flag is used instead of strchr(sender, '@') for speed and * also since username/nick may have had a '@' in them. -avalon */ #if defined(ESNET_NEG) if (!flag && MyConnect(from) && user->host && !(to->cli_connect->negociacion & USER_TOK)) #else if (!flag && MyConnect(from) && user->host) #endif { strcat(sender, "@"); #if defined(BDD_VIP) strcat(sender, get_visiblehost(from, NULL)); #else if (IsUnixSocket(from)) strcat(sender, user->host); else strcat(sender, from->sockhost); #endif } *sendbuf = ':'; strcpy(&sendbuf[1], sender); /* Assuming 'pattern' always starts with ":%s ..." */ vsprintf_irc(sendbuf + strlen(sendbuf), &pattern[3], vl); } else
void GJJoinIdPassRequest(LPSDHP_IDPASS lpMsgIdPass,int aIndex ) { char szId[MAX_IDSTRING+1]; #if (S6EP2 == 1 || EX700==1) char szPass[20+1]; #else char szPass[MAX_IDSTRING+1]; #endif char szIpAddress[17]; char szJoominNumber[21]=""; BYTE result=0x01; int sbOfs=0; int vNumber=0; int UserNumber=-1; int DBNumber; SDHP_IDPASSRESULT spResult; memset(szId, 0, sizeof(szId)); memset(szPass, 0, sizeof(szPass)); memset(szIpAddress, 0, sizeof(szIpAddress)); memcpy(szId,lpMsgIdPass->Id,MAX_IDSTRING); memcpy(szPass,lpMsgIdPass->Pass,20); BuxConvert(szId, MAX_IDSTRING); BuxConvert(szPass, 20); char Block='0'; if( SQLSyntexCheck(szId) == FALSE || SQLSyntexCheck(szPass) == FALSE || SpaceSyntexCheck(szId) == FALSE) { result = 0x02; } else { EnterCriticalSection(&g_JSData.m_critJoinUser); int nRet = IsUser(szId,szPass,szJoominNumber,Block,DBNumber); if( nRet == 0 ) { result = 0; } if(gObjSearchUser(szId) == -1) { if( nRet == 1 && Block > '0' ) { if( Block == 'A' ) { result=0x0E; } else if( Block == 'B' ) { result=0x0F; } else result=0x05; } else if(nRet == 1) { UserNumber = gObjAdd(szId,szPass,DBNumber,aIndex,lpMsgIdPass->IpAddress,lpMsgIdPass->Number); if(UserNumber < 0) { result = 0x04; } else { gObj[UserNumber].bloc_code = Block; memcpy(gObj[UserNumber].JoomingNumber,szJoominNumber,20); g_Window.ServerLogAdd(ST_JOINSERVER,"[ %s ] User login",szId); } } else if(nRet == 0) { result = 0x00; } else { result = 0x02; } } else { if( result == 0 ) { result = 0x03; } else { result = 0x06; } g_Window.ServerLogAdd(ST_JOINSERVER,"[ %s ] [ERROR] User Connected login",szId); } LeaveCriticalSection(&g_JSData.m_critJoinUser); } spResult.h.size = sizeof( spResult ); spResult.h.c = PMHC_BYTE; spResult.h.headcode = 0x01; spResult.result = result; spResult.Number = lpMsgIdPass->Number; spResult.UserNumber = UserNumber; spResult.DBNumber = DBNumber; memcpy(spResult.JoominNumber, szJoominNumber, 20); memcpy(spResult.Id, szId, MAX_IDSTRING); DataSend(aIndex,(LPBYTE)&spResult,spResult.h.size); }
static void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr; struct Client *acptr2; const struct ConnectionClass* cl; char* tname; int doall; int *link_s; int *link_u; int cnt = 0; int wilds; int dow; if (parc < 2 || BadPtr(parv[1])) { /* just "TRACE" without parameters. Must be from local client */ parc = 1; acptr = &me; tname = cli_name(&me); i = HUNTED_ISME; } else if (parc < 3 || BadPtr(parv[2])) { /* No target specified. Make one before propagating. */ parc = 2; tname = parv[1]; if ((acptr = find_match_server(parv[1])) || ((acptr = FindClient(parv[1])) && !MyUser(acptr))) { if (IsUser(acptr)) parv[2] = cli_name(cli_user(acptr)->server); else parv[2] = cli_name(acptr); parc = 3; parv[3] = 0; if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr), "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; } else i = HUNTED_ISME; } else { /* Got "TRACE <tname> :<target>" */ parc = 3; if (MyUser(sptr) || Protocol(cptr) < 10) acptr = find_match_server(parv[2]); else acptr = FindNServer(parv[2]); if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; tname = parv[1]; } if (i == HUNTED_PASS) { if (!acptr) acptr = next_client(GlobalClientList, tname); else acptr = cli_from(acptr); send_reply(sptr, RPL_TRACELINK, version, debugmode, tname, acptr ? cli_name(cli_from(acptr)) : "<No_match>"); return; } doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; /* Don't give (long) remote listings to lusers */ if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) { send_reply(sptr, RPL_TRACEEND); return; } link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0])); link_u = link_s + maxconnections; if (doall) { for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (IsUser(acptr)) link_u[cli_fd(cli_from(acptr))]++; else if (IsServer(acptr)) link_s[cli_fd(cli_from(acptr))]++; } } /* report all direct connections */ for (i = 0; i <= HighestFd; i++) { const char *conClass; if (!(acptr = LocalClientArray[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && match(tname, cli_name(acptr))) continue; if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr))) continue; conClass = get_client_class(acptr); switch (cli_status(acptr)) { case STAT_CONNECTING: send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr)); cnt++; break; case STAT_HANDSHAKE: send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr)); cnt++; break; case STAT_ME: break; case STAT_UNKNOWN: case STAT_UNKNOWN_USER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, get_client_name(acptr, HIDE_IP)); cnt++; break; case STAT_UNKNOWN_SERVER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server"); cnt++; break; case STAT_USER: /* Only opers see users if there is a wildcard but anyone can see all the opers. */ if ((IsAnOper(sptr) && (MyUser(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) send_reply(sptr, RPL_TRACEOPERATOR, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); else send_reply(sptr, RPL_TRACEUSER, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); cnt++; } break; /* * Connection is a server * * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age> * * class Class the server is in * nS Number of servers reached via this link * nC Number of clients reached via this link * name Name of the server linked * ConnBy Who established this link * last Seconds since we got something from this link * age Seconds this link has been alive * * Additional comments etc...... -Cym-<*****@*****.**> */ case STAT_SERVER: if (cli_serv(acptr)->user) { if (!cli_serv(acptr)->by[0] || !(acptr2 = findNUser(cli_serv(acptr)->by)) || (cli_user(acptr2) != cli_serv(acptr)->user)) acptr2 = NULL; send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), acptr2 ? cli_name(acptr2) : "*", cli_serv(acptr)->user->username, cli_serv(acptr)->user->host, CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); } else send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*", cli_name(&me), CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); cnt++; break; default: /* We actually shouldn't come here, -msa */ send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP)); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (IsAnOper(sptr) && doall) { for (cl = get_class_list(); cl; cl = cl->next) { if (Links(cl) > 1) send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1); } } send_reply(sptr, RPL_TRACEEND); MyFree(link_s); }
/** Parse a line of data from a user. * NOTE: parse_*() should not be called recursively by any other * functions! * @param[in] cptr Client that sent the data. * @param[in] buffer Start of input line. * @param[in] bufend End of input line. * @return 0 on success, -1 on parse error, or CPTR_KILLED if message * handler returns it. */ int parse_client(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch; char* s; int i; int paramcount; int isshun = 0; int lagmin = -1; int lagfactor = -1; struct Message* mptr; MessageHandler handler = 0; Debug((DEBUG_DEBUG, "Client Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */ if (*ch == ':') /* Is any client doing this ? */ { for (++ch; *ch && *ch != ' '; ++ch) ; /* Ignore sender prefix from client */ while (*ch == ' ') ch++; /* Advance to command */ } if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } if ((s = strchr(ch, ' '))) *s++ = '\0'; expire_shuns(); if (IsRegistered(cptr)) { if (cli_user(cptr)->username && cli_user(cptr)->host) { if (shun_lookup(cptr, 0)) isshun = 1; } } if ((mptr = msg_tree_parse(ch, &msg_tree)) == NULL) { /* * 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 */ if (buffer[0] != '\0' && !isshun) { if (IsUser(from)) send_reply(from, ERR_UNKNOWNCOMMAND, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } ServerStats->is_unco++; return (-1); } if (isshun && !(mptr->flags & MFLG_NOSHUN)) return 0; paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; lagmin = get_lag_min(cptr); lagfactor = get_lag_factor(cptr); if (lagmin < 0) lagmin = 2; if (lagfactor < 0) lagfactor = 120; if (((mptr->flags & MFLG_SLOW) || !IsAnOper(cptr)) && lagfactor > 0) cli_since(cptr) += (lagmin + i / lagfactor); /* * 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 */ /* * 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' !! */ if (mptr->flags & MFLG_EXTRA) { /* This is a horrid kludge to avoid changing the command handler * argument list. */ para[1] = (char*)mptr->extra; i = 1; } else { i = 0; } if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; ++mptr->count; handler = mptr->handlers[cli_handler(cptr)]; assert(0 != handler); if (!feature_bool(FEAT_IDLE_FROM_MSG) && IsUser(cptr) && handler != m_ping && handler != m_ignore) cli_user(from)->last = CurrentTime; return (*handler) (cptr, from, i, para); }
/** * 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; }
int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; struct Channel *ch2ptr; struct Client *c2ptr; struct Membership* member; char* s; char* para = parc > 1 ? parv[1] : 0; int showingdelayed = 0; if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { para = (parc > 2) ? parv[2] : 0; showingdelayed = NAMES_DEL; if (parc > 3 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %s %C", 3, parc, parv)) return 0; } else if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) return 0; if (EmptyString(para)) { send_reply(sptr, RPL_ENDOFNAMES, "*"); return 0; } do { s = strchr(para, ','); if (s) *s++ = '\0'; /* * Special Case 1: "/names 0". * Full list as per RFC. */ if ((*para == '0') || (*para == '\0')) { int idx; int mlen; int flag; struct Channel *ch3ptr; char buf[BUFSIZE]; mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); /* List all visible channels/visible members */ for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next) { if (!ShowChannel(sptr, ch2ptr)) continue; /* Don't show secret chans. */ else if (find_channel_member(sptr, ch2ptr)) do_names(sptr, ch2ptr, showingdelayed|NAMES_ALL); /* Full list if we're in this chan. */ else do_names(sptr, ch2ptr, showingdelayed|NAMES_VIS); } /* List all remaining users on channel '*' */ strcpy(buf, "* * :"); idx = 5; flag = 0; for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr)) { int showflag = 0; if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr))) continue; member = cli_user(c2ptr)->channel; while (member) { ch3ptr = member->channel; if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr)) showflag = 1; member = member->next_channel; } if (showflag) /* Have we already shown them? */ continue; strcpy(buf + idx, cli_name(c2ptr)); idx += strlen(cli_name(c2ptr)); buf[idx++] = ' '; flag = 1; if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */ { send_reply(sptr, RPL_NAMREPLY, buf); strcpy(buf, "* * :"); idx = 5; flag = 0; } } if (flag) send_reply(sptr, RPL_NAMREPLY, buf); send_reply(sptr, RPL_ENDOFNAMES, "*"); } else if ((chptr = FindChannel(para)) != NULL) { member = find_member_link(chptr, sptr); if (member) { /* * Special Case 2: User is on this channel, requesting full names list. * (As performed with each /join) - ** High frequency usage ** */ do_names(sptr, chptr, showingdelayed|NAMES_ALL|NAMES_EON); } else { /* * Special Case 3: User isn't on this channel, show all visible users, in * non secret channels. */ do_names(sptr, chptr, showingdelayed|NAMES_VIS|NAMES_EON); } } else send_reply(sptr, RPL_ENDOFNAMES, para); } while ((para = s) != NULL); return 1; }
/* * m_who - generic message handler * * parv[0] = sender prefix * parv[1] = nickname mask list * parv[2] = additional selection flag, only 'o' for now. * and %flags to specify what fields to output * plus a ,querytype if the t flag is specified * so the final thing will be like o%tnchu,777 * parv[3] = _optional_ parameter that overrides parv[1] * This can be used as "/quote who foo % :The Black Hacker * to find me, parv[3] _can_ contain spaces !. */ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; /* The mask we are looking for */ char ch; /* Scratch char register */ struct Channel *chptr; /* Channel to show */ struct Client *acptr; /* Client to show */ int bitsel; /* Mask of selectors to apply */ int matchsel; /* Wich fields the match should apply on */ int counter; /* Query size counter, initially used to count fields */ int commas; /* Does our mask contain any comma ? If so is a list.. */ int fields; /* Mask of fields to show */ int isthere = 0; /* When this set the user is member of chptr */ char *nick; /* Single element extracted from the mask list */ char *p; /* Scratch char pointer */ char *qrt; /* Pointer to the query type */ static char mymask[512]; /* To save the mask before corrupting it */ unsigned int who_marker; /* Used to mark clients we've touched */ /* Let's find where is our mask, and if actually contains something */ mask = ((parc > 1) ? parv[1] : 0); if (parc > 3 && parv[3]) mask = parv[3]; if (mask && ((mask[0] == '\0') || (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*'))))) mask = 0; /* Evaluate the flags now, we consider the second parameter as "matchFlags%fieldsToInclude,querytype" */ bitsel = fields = counter = matchsel = 0; qrt = 0; if (parc > 2 && parv[2] && *parv[2]) { p = parv[2]; while (((ch = *(p++))) && (ch != '%') && (ch != ',')) switch (ch) { case 'o': case 'O': bitsel |= WHOSELECT_OPER; continue; case 'x': case 'X': if (HasPriv(sptr, PRIV_WHOX)) { log_write(LS_WHO, L_INFO, LOG_NOSNOTICE, "%#C WHO %s %s", sptr, (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]); bitsel |= WHOSELECT_EXTRA; } continue; case 'n': case 'N': matchsel |= WHO_FIELD_NIC; continue; case 'u': case 'U': matchsel |= WHO_FIELD_UID; continue; case 'h': case 'H': matchsel |= WHO_FIELD_HOS; continue; case 'i': case 'I': matchsel |= WHO_FIELD_NIP; continue; case 's': case 'S': matchsel |= WHO_FIELD_SER; continue; case 'r': case 'R': matchsel |= WHO_FIELD_REN; continue; case 'a': case 'A': matchsel |= WHO_FIELD_ACC; continue; } if (ch == '%') while ((ch = *p++) && (ch != ',')) { counter++; switch (ch) { case 'c': case 'C': fields |= WHO_FIELD_CHA; break; case 'd': case 'D': fields |= WHO_FIELD_DIS; break; case 'f': case 'F': fields |= WHO_FIELD_FLA; break; case 'h': case 'H': fields |= WHO_FIELD_HOS; break; case 'i': case 'I': fields |= WHO_FIELD_NIP; break; case 'l': case 'L': fields |= WHO_FIELD_IDL; case 'n': case 'N': fields |= WHO_FIELD_NIC; break; case 'r': case 'R': fields |= WHO_FIELD_REN; break; case 's': case 'S': fields |= WHO_FIELD_SER; break; case 't': case 'T': fields |= WHO_FIELD_QTY; break; case 'u': case 'U': fields |= WHO_FIELD_UID; break; case 'a': case 'A': fields |= WHO_FIELD_ACC; break; default: break; } }; if (ch) qrt = p; } if (!matchsel) matchsel = WHO_FIELD_DEF; if (!fields) counter = 7; if (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr)) matchsel &= ~WHO_FIELD_SER; if (qrt && (fields & WHO_FIELD_QTY)) { p = qrt; if (!((*p > '9') || (*p < '0'))) p++; if (!((*p > '9') || (*p < '0'))) p++; if (!((*p > '9') || (*p < '0'))) p++; *p = '\0'; } else qrt = 0; /* I'd love to add also a check on the number of matches fields per time */ counter = (2048 / (counter + 4)); if (mask && (strlen(mask) > 510)) mask[510] = '\0'; who_marker = get_client_marker(); commas = (mask && strchr(mask, ',')); /* First treat mask as a list of plain nicks/channels */ if (mask) { strcpy(mymask, mask); for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick; nick = ircd_strtok(&p, 0, ",")) { if (IsChannelName(nick) && (chptr = FindChannel(nick))) { isthere = (find_channel_member(sptr, chptr) != 0); if (isthere || SEE_CHANNEL(sptr, chptr, bitsel)) { struct Membership* member; for (member = chptr->members; member; member = member->next_member) { acptr = member->user; if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if ((acptr != sptr) && (member->status & CHFL_ZOMBIE)) continue; if (!(isthere || (SEE_USER(sptr, acptr, bitsel)))) continue; if (!Process(acptr)) /* This can't be moved before other checks */ continue; if (!(isthere || (IsOper(sptr) && (bitsel & WHOSELECT_EXTRA) && HasPriv(sptr, PRIV_SEE_CHAN)) || (SHOW_MORE(sptr, counter)))) break; do_who(sptr, acptr, chptr, fields, qrt); } } } else { if ((acptr = FindUser(nick)) && ((!(bitsel & WHOSELECT_OPER)) || SeeOper(sptr,acptr)) && Process(acptr) && SHOW_MORE(sptr, counter)) { do_who(sptr, acptr, 0, fields, qrt); } } } } /* If we didn't have any comma in the mask treat it as a real mask and try to match all relevant fields */ if (!(commas || (counter < 1))) { int minlen, cset; static struct in_mask imask; if (mask) { matchcomp(mymask, &minlen, &cset, mask); if (matchcompIP(&imask, mask)) matchsel &= ~WHO_FIELD_NIP; if ((minlen > NICKLEN) || !(cset & NTL_IRCNK)) matchsel &= ~WHO_FIELD_NIC; if ((matchsel & WHO_FIELD_SER) && ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN)) || (!markMatchexServer(mymask, minlen)))) matchsel &= ~WHO_FIELD_SER; if ((minlen > USERLEN) || !(cset & NTL_IRCUI)) matchsel &= ~WHO_FIELD_UID; if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN)) matchsel &= ~WHO_FIELD_HOS; } /* First of all loop through the clients in common channels */ if ((!(counter < 1)) && matchsel) { struct Membership* member; struct Membership* chan; for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) { chptr = chan->channel; for (member = chptr->members; member; member = member->next_member) { acptr = member->user; if (!(IsUser(acptr) && Process(acptr))) continue; /* Now Process() is at the beginning, if we fail we'll never have to show this acptr in this query */ if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if ((mask) && ((!(matchsel & WHO_FIELD_NIC)) || matchexec(cli_name(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_UID)) || matchexec(cli_user(acptr)->username, mymask, minlen)) && ((!(matchsel & WHO_FIELD_SER)) || (!HasFlag(cli_user(acptr)->server, FLAG_MAP))) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr)) || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) != imask.bits.s_addr)) || (imask.fall && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen))))) continue; if (!SHOW_MORE(sptr, counter)) break; do_who(sptr, acptr, chptr, fields, qrt); } } } /* Loop through all clients :-\, if we still have something to match to and we can show more clients */ if ((!(counter < 1)) && matchsel) for (acptr = cli_prev(&me); acptr; acptr = cli_prev(acptr)) { if (!(IsUser(acptr) && Process(acptr))) continue; if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if (!(SEE_USER(sptr, acptr, bitsel))) continue; if ((mask) && ((!(matchsel & WHO_FIELD_NIC)) || matchexec(cli_name(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_UID)) || matchexec(cli_user(acptr)->username, mymask, minlen)) && ((!(matchsel & WHO_FIELD_SER)) || (!HasFlag(cli_user(acptr)->server, FLAG_MAP))) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) || (HasHiddenHost(acptr) && !IsAnOper(sptr)) || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) != imask.bits.s_addr)) || (imask.fall && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen))))) continue; if (!SHOW_MORE(sptr, counter)) break; do_who(sptr, acptr, 0, fields, qrt); } } /* Make a clean mask suitable to be sent in the "end of" */ if (mask && (p = strchr(mask, ' '))) *p = '\0'; send_reply(sptr, RPL_ENDOFWHO, BadPtr(mask) ? "*" : mask); /* Notify the user if we decided that his query was too long */ if (counter < 0) send_reply(sptr, ERR_QUERYTOOLONG, "WHO"); return 0; }
/* Rewritten by Run - 24 sept 94 */ static void exit_one_client(struct Client* bcptr, const char* comment) { struct SLink *lp; struct Ban *bp; if (cli_serv(bcptr) && cli_serv(bcptr)->client_list) /* Was SetServerYXX called ? */ ClearServerYXX(bcptr); /* Removes server from server_list[] */ if (IsUser(bcptr)) { /* * clear out uping requests */ if (IsUPing(bcptr)) uping_cancel(bcptr, 0); /* * Stop a running /LIST clean */ if (MyUser(bcptr) && cli_listing(bcptr)) { MyFree(cli_listing(bcptr)); cli_listing(bcptr) = NULL; } /* * If a person is on a channel, send a QUIT notice * to every client (person) on the same channel (so * that the client can show the "**signoff" message). * (Note: The notice is to the local clients *only*) */ sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment); remove_user_from_all_channels(bcptr); /* Clean up invitefield */ while ((lp = cli_user(bcptr)->invited)) del_invite(bcptr, lp->value.chptr); /* Clean up silencefield */ while ((bp = cli_user(bcptr)->silence)) { cli_user(bcptr)->silence = bp->next; free_ban(bp); } /* Clean up snotice lists */ if (MyUser(bcptr)) set_snomask(bcptr, ~0, SNO_DEL); if (IsInvisible(bcptr)) { assert(UserStats.inv_clients > 0); --UserStats.inv_clients; } if (IsOper(bcptr)) { assert(UserStats.opers > 0); --UserStats.opers; } if (MyConnect(bcptr)) Count_clientdisconnects(bcptr, UserStats); else Count_remoteclientquits(UserStats, bcptr); } else if (IsServer(bcptr)) { /* Remove downlink list node of uplink */ remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown); cli_serv(bcptr)->updown = 0; if (MyConnect(bcptr)) Count_serverdisconnects(UserStats); else Count_remoteserverquits(UserStats); } else if (IsMe(bcptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self! */ } else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr)) Count_unknowndisconnects(UserStats); /* * Update IPregistry */ if (IsIPChecked(bcptr)) IPcheck_disconnect(bcptr); /* * Remove from serv->client_list * NOTE: user is *always* NULL if this is a server */ if (cli_user(bcptr)) { assert(!IsServer(bcptr)); /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */ RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr)); } /* Remove bcptr from the client list */ #ifdef DEBUGMODE if (hRemClient(bcptr) != 0) Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p", bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host", cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr), cli_status(bcptr), cli_user(bcptr))); #else hRemClient(bcptr); #endif remove_client_from_list(bcptr); }
/** 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 | CONF_UWORLD); 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_butone(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_butone(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; }
/** 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); }
/** 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)); }
/** 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; }
/** Start a connection to another server. * @param aconf Connect block data for target server. * @param by Client who requested the connection (if any). * @return Non-zero on success; zero on failure. */ int connect_server(struct ConfItem* aconf, struct Client* by) { struct Client* cptr = 0; assert(0 != aconf); if (aconf->dns_pending) { sendto_opmask_butone(0, SNO_OLDSNO, "Server %s connect DNS pending", aconf->name); return 0; } Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name, ircd_ntoa(&aconf->address.addr))); if ((cptr = FindClient(aconf->name))) { if (IsServer(cptr) || IsMe(cptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "Server %s already present from %s", aconf->name, cli_name(cli_from(cptr))); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present " "from %s", by, aconf->name, cli_name(cli_from(cptr))); } return 0; } else if (IsHandshake(cptr) || IsConnecting(cptr)) { if (by && IsUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in " "progress", by, cli_name(cptr)); } return 0; } } /* * If we don't know the IP# for this host and it is a hostname and * not a ip# string, then try and find the appropriate host record. */ if (!irc_in_addr_valid(&aconf->address.addr) && !ircd_aton(&aconf->address.addr, aconf->host)) { char buf[HOSTLEN + 1]; host_from_uh(buf, aconf->host, HOSTLEN); gethost_byname(buf, connect_dns_callback, aconf); aconf->dns_pending = 1; return 0; } cptr = make_client(NULL, STAT_UNKNOWN_SERVER); /* * Copy these in so we have something for error detection. */ ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN); ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN); /* * Attach config entries to client here rather than in * completed_connection. This to avoid null pointer references */ attach_confs_byhost(cptr, aconf->host, CONF_SERVER); if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) { sendto_opmask_butone(0, SNO_OLDSNO, "Host %s is not enabled for " "connecting: no Connect block", aconf->name); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no " "Connect block", by, aconf->name); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * attempt to connect to the server in the conf line */ if (!connect_inet(aconf, cptr)) { if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by, cli_name(cptr)); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * NOTE: if we're here we have a valid C:Line and the client should * have started the connection and stored the remote address/port and * ip address name in itself * * The socket has been connected or connect is in progress. */ make_server(cptr); if (by && IsUser(by)) { ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s", NumNick(by)); assert(0 == cli_serv(cptr)->user); cli_serv(cptr)->user = cli_user(by); cli_user(by)->refcnt++; } else { *(cli_serv(cptr))->by = '\0'; /* strcpy(cptr->serv->by, "Auto"); */ } cli_serv(cptr)->up = &me; SetConnecting(cptr); if (cli_fd(cptr) > HighestFd) HighestFd = cli_fd(cptr); LocalClientArray[cli_fd(cptr)] = cptr; Count_newunknown(UserStats); /* Actually we lie, the connect hasn't succeeded yet, but we have a valid * cptr, so we register it now. * Maybe these two calls should be merged. */ add_client_to_list(cptr); hAddClient(cptr); /* nextping = CurrentTime; */ return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ? completed_connection(cptr) : 1; }
/** Read a 'packet' of data from a connection and process it. Read in * 8k chunks to give a better performance rating (for server * connections). Do some tricky stuff for client connections to make * sure they don't do any flooding >:-) -avalon * @param cptr Client from which to read data. * @param socket_ready If non-zero, more data can be read from the client's socket. * @return Positive number on success, zero on connection-fatal failure, negative * if user is killed. */ static int read_packet(struct Client *cptr, int socket_ready) { unsigned int dolen = 0; unsigned int length = 0; if (socket_ready && !(IsUser(cptr) && !IsOper(cptr) && DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) { switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) { case IO_SUCCESS: if (length) { cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); ClrFlag(cptr, FLAG_NONL); if (cli_lasttime(cptr) > cli_since(cptr)) cli_since(cptr) = cli_lasttime(cptr); } break; case IO_BLOCKED: break; case IO_FAILURE: cli_error(cptr) = errno; /* SetFlag(cptr, FLAG_DEADSOCKET); */ return 0; } } /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ if (length > 0 && IsServer(cptr)) return server_dopacket(cptr, readbuf, length); else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr))) return connect_dopacket(cptr, readbuf, length); else { /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its * turn comes around. */ if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); if (IsUser(cptr)) { if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD) && !IsOper(cptr)) return exit_client(cptr, cptr, &me, "Excess Flood"); } while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10)) { dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* * Devious looking...whats it do ? well..if a client * sends a *long* message without any CR or LF, then * dbuf_getmsg fails and we pull it out using this * loop which just gets the next 512 bytes and then * deletes the rest of the buffer contents. * -avalon */ if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { /* More than 512 bytes in the line - drop the input and yell * at the client. */ DBufClear(&(cli_recvQ(cptr))); send_reply(cptr, ERR_INPUTTOOLONG); } } else if (client_dopacket(cptr, dolen) == CPTR_KILLED) return CPTR_KILLED; /* * If it has become registered as a Server * then skip the per-message parsing below. */ if (IsHandshake(cptr) || IsServer(cptr)) { while (-1) { dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf)); if (dolen <= 0) return 1; else if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else DBufClear(&(cli_recvQ(cptr))); } else if ((IsServer(cptr) && server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) || (!IsServer(cptr) && connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED)) return CPTR_KILLED; } } } /* If there's still data to process, wait 2 seconds first */ if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && !t_onqueue(&(cli_proc(cptr)))) { Debug((DEBUG_LIST, "Adding client process timer for %C", cptr)); cli_freeflag(cptr) |= FREEFLAG_TIMER; timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr), TT_RELATIVE, 2); } } return 1; }
/** 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 (-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; } 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; } }