/* * ms_links - server message handler * * parv[0] = sender prefix * parv[1] = servername mask * * or * * parv[0] = sender prefix * parv[1] = server to query * parv[2] = servername mask */ int ms_links(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; if (parc > 2) { if (hunt_server_cmd(sptr, CMD_LINKS, cptr, 1, "%C :%s", 1, parc, parv) != HUNTED_ISME) return 0; mask = parv[2]; } else mask = parc < 2 ? 0 : parv[1]; for (acptr = GlobalClientList, collapse(mask); acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (!BadPtr(mask) && match(mask, cli_name(acptr))) continue; send_reply(sptr, RPL_LINKS, cli_name(acptr), cli_name(cli_serv(acptr)->up), cli_hopcount(acptr), cli_serv(acptr)->prot, ((cli_info(acptr))[0] ? cli_info(acptr) : "(Unknown Location)")); } send_reply(sptr, RPL_ENDOFLINKS, BadPtr(mask) ? "*" : mask); return 0; }
/** 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; }
/** Mark a client as dead, even if they are not the current message source. * This is done by setting the DEADSOCKET flag on the user and letting the * main loop perform the actual exit logic. * @param[in,out] to Client being killed. * @param[in] notice Message for local opers. */ static void dead_link(struct Client *to, char *notice) { SetFlag(to, FLAG_DEADSOCKET); /* * If because of BUFFERPOOL problem then clean dbuf's now so that * notices don't hurt operators below. */ DBufClear(&(cli_recvQ(to))); MsgQClear(&(cli_sendQ(to))); client_drop_sendq(cli_connect(to)); /* * Keep a copy of the last comment, for later use... */ ircd_strncpy(cli_info(to), notice, REALLEN); if (!IsUser(to) && !IsUnknown(to) && !HasFlag(to, FLAG_CLOSING)) sendto_opmask_butone(0, SNO_OLDSNO, "%s for %s", cli_info(to), cli_name(to)); Debug((DEBUG_ERROR, cli_info(to))); }
/** Handle received data from a new (unregistered) connection. * @param[in] cptr Unregistered connection that sent us data. * @param[in] buffer Input buffer. * @param[in] length Number of bytes in input buffer. * @return 1 on success or CPTR_KILLED if the client is squit. */ int connect_dopacket(struct Client *cptr, const char *buffer, int length) { const char* src; char* endp; char* client_buffer; assert(0 != cptr); update_bytes_received(cptr, length); client_buffer = cli_buffer(cptr); endp = client_buffer + cli_count(cptr); src = buffer; while (length-- > 0) { *endp = *src++; /* * Yuck. Stuck. To make sure we stay backward compatible, * we must assume that either CR or LF terminates the message * and not CR-LF. By allowing CR or LF (alone) into the body * of messages, backward compatibility is lost and major * problems will arise. - Avalon */ if (IsEol(*endp)) { /* Skip extra LF/CR's */ if (endp == client_buffer) continue; *endp = '\0'; update_messages_received(cptr); if (parse_client(cptr, cli_buffer(cptr), endp) == CPTR_KILLED) return CPTR_KILLED; /* Socket is dead so exit */ if (IsDead(cptr)) return exit_client(cptr, cptr, &me, cli_info(cptr)); else if (IsServer(cptr)) { cli_count(cptr) = 0; return server_dopacket(cptr, src, length); } endp = client_buffer; } else if (endp < client_buffer + BUFSIZE) /* There is always room for the null */ ++endp; } cli_count(cptr) = endp - cli_buffer(cptr); return 1; }
/* * m_links - generic message handler * * parv[0] = sender prefix * parv[1] = servername mask * * or * * parv[0] = sender prefix * parv[1] = server to query * parv[2] = servername mask */ int m_links(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; struct Client *acptr; if (feature_bool(FEAT_HIS_LINKS) && !IsAnOper(sptr)) { send_reply(sptr, RPL_ENDOFLINKS, parc < 2 ? "*" : parv[1]); sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s %s", sptr, "/LINKS has been disabled, from CFV-165. Visit ", feature_str(FEAT_HIS_URLSERVERS)); return 0; } if (parc > 2) { if (hunt_server_cmd(sptr, CMD_LINKS, cptr, 1, "%C :%s", 1, parc, parv) != HUNTED_ISME) return 0; mask = parv[2]; } else mask = parc < 2 ? 0 : parv[1]; for (acptr = GlobalClientList, collapse(mask); acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (!BadPtr(mask) && match(mask, cli_name(acptr))) continue; send_reply(sptr, RPL_LINKS, cli_name(acptr), cli_name(cli_serv(acptr)->up), cli_hopcount(acptr), cli_serv(acptr)->prot, ((cli_info(acptr))[0] ? cli_info(acptr) : "(Unknown Location)")); } send_reply(sptr, RPL_ENDOFLINKS, BadPtr(mask) ? "*" : mask); return 0; }
/** Handle received data from a local client. * @param[in] cptr Local client that sent us data. * @param[in] length Total number of bytes in client's input buffer. * @return 1 on success or CPTR_KILLED if the client is squit. */ int client_dopacket(struct Client *cptr, unsigned int length) { assert(0 != cptr); update_bytes_received(cptr, length); update_messages_received(cptr); if (CPTR_KILLED == parse_client(cptr, cli_buffer(cptr), cli_buffer(cptr) + length)) return CPTR_KILLED; else if (IsDead(cptr)) return exit_client(cptr, cptr, &me, cli_info(cptr)); return 1; }
static void stats_servers_verbose(struct Client* sptr, struct StatDesc* sd, int stat, char* param) { struct Client *acptr; /* lowercase 'v' is for human-readable, * uppercase 'V' is for machine-readable */ if (stat == 'v') send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, "%-20s %-20s Flags Hops Numeric Lag RTT Up Down " "Clients/Max Proto %-10s :Info", "Servername", "Uplink", "LinkTS"); for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (param && match(param, cli_name(acptr))) /* narrow search */ continue; send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ? "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i " "P%-2i %Tu :%s" : "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s", cli_name(acptr), cli_name(cli_serv(acptr)->up), IsBurst(acptr) ? 'B' : '-', IsBurstAck(acptr) ? 'A' : '-', IsHub(acptr) ? 'H' : '-', IsService(acptr) ? 'S' : '-', cli_hopcount(acptr), NumServ(acptr), base64toint(cli_yxx(acptr)), cli_serv(acptr)->lag, cli_serv(acptr)->asll_rtt, cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from, cli_serv(acptr)->clients, cli_serv(acptr)->nn_mask, cli_serv(acptr)->prot, cli_serv(acptr)->timestamp, cli_info(acptr)); } }
/** 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; }
/** Handle an update to FEAT_HIS_SERVERINFO. */ static void feature_notify_serverinfo(void) { ircd_strncpy(cli_info(&his), feature_str(FEAT_HIS_SERVERINFO), REALLEN); }
/** 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)); }
/** Handle a SERVER message from another server. * * \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 ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { int i; char* host; struct Client* acptr; struct Client* bcptr; int hop; int ret; unsigned short prot; time_t start_timestamp; time_t timestamp; if (parc < 8) { return 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); } /* * 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) return exit_client_msg(cptr, sptr, &me, "Bogus timestamps (%s %s)", parv[3], parv[4]); if (parv[parc - 1][0] == '\0') return exit_client_msg(cptr, cptr, &me, "No server info specified for %s", host); ret = check_loop_and_lh(cptr, sptr, NULL, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, parv[5][0] == 'J'); if (ret != 1) return ret; /* * 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... */ acptr = make_client(cptr, STAT_SERVER); make_server(acptr); cli_serv(acptr)->prot = prot; cli_serv(acptr)->timestamp = timestamp; cli_hopcount(acptr) = hop; ircd_strncpy(cli_name(acptr), host, HOSTLEN); ircd_strncpy(cli_info(acptr), parv[parc-1], REALLEN); cli_serv(acptr)->up = sptr; cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr); /* Use cptr, because we do protocol 9 -> 10 translation for numeric nicks ! */ SetServerYXX(cptr, acptr, parv[6]); update_uworld_flags(cptr); if (*parv[7] == '+') set_server_flags(acptr, parv[7] + 1); Count_newremoteserver(UserStats); add_client_to_list(acptr); hAddClient(acptr); if (*parv[5] == 'J') { SetBurst(acptr); SetJunction(acptr); for (bcptr = cli_serv(acptr)->up; !IsMe(bcptr); bcptr = cli_serv(bcptr)->up) if (IsBurstOrBurstAck(bcptr)) break; if (IsMe(bcptr)) sendto_opmask(0, SNO_NETWORK, "Net junction: %s %s", cli_name(sptr), cli_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 <= HighestFd; i++) { if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || bcptr == cptr || IsMe(bcptr)) continue; if (0 == match(cli_name(&me), cli_name(acptr))) continue; sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s%s :%s", cli_name(acptr), hop + 1, parv[4], parv[5], NumServCap(acptr), IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "", cli_info(acptr)); } return 0; }
/** * 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; }
void checkClient(struct Client *sptr, struct Client *acptr) { struct Channel *chptr; struct Membership *lp; char outbuf[BUFSIZE]; char *privs; time_t nowr; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "user", acptr->cli_name); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick:: %s (%s%s)", acptr->cli_name, NumNick(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (MyUser(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on:: %s", myctime(acptr->cli_firsttime)); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp:: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " User/Hostmask:: %s@%s (%s)", acptr->cli_user->username, acptr->cli_user->host, ircd_ntoa((const char*) &(cli_ip(acptr)))); send_reply(sptr, RPL_DATASTR, outbuf); if (((feature_int(FEAT_HOST_HIDING_STYLE) == 1) ? HasHiddenHost(acptr) : IsHiddenHost(acptr)) || IsSetHost(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Real User/Host:: %s@%s", acptr->cli_user->realusername, acptr->cli_user->realhost); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name:: %s%c", cli_info(acptr), COLOR_OFF); send_reply(sptr, RPL_DATASTR, outbuf); if (IsService(cli_user(acptr)->server)) { if (acptr) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsAdmin(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: IRC Administrator (service)"); else if (IsAnOper(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator (service)"); else send_reply(sptr, RPL_DATASTR, " Status:: Client (service)"); } else if (IsAdmin(acptr)) { send_reply(sptr, RPL_DATASTR, " Status:: IRC Administrator"); } else if (IsAnOper(acptr)) { send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator"); } else { send_reply(sptr, RPL_DATASTR, " Status:: Client"); } if (MyUser(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Class:: %s", get_client_class(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } privs = client_print_privs(acptr); if (strlen(privs) > 1) client_check_privs(acptr, sptr); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s", cli_name(acptr->cli_user->server)); send_reply(sptr, RPL_DATASTR, outbuf); if (cli_version(acptr)) { if (strlen(cli_version(acptr)) > 0) { ircd_snprintf(0, outbuf, sizeof(outbuf), " CTCP Version:: %s", cli_version(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } if (cli_user(acptr) && !EmptyString(cli_user(acptr)->swhois)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " SWHOIS:: %s", cli_user(acptr)->swhois); send_reply(sptr, RPL_DATASTR, outbuf); } if (cli_webirc(acptr)) { if (strlen(cli_webirc(acptr)) > 0) { ircd_snprintf(0, outbuf, sizeof(outbuf), " WebIRC:: %s", cli_webirc(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } if (cli_sslclifp(acptr) && (strlen(cli_sslclifp(acptr)) > 0)) { ircd_snprintf(0, outbuf, sizeof(outbuf), "SSL Fingerprint:: %s", cli_sslclifp(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } if (MyUser(acptr)) get_eflags(sptr, acptr); /* +s (SERV_NOTICE) is not relayed to us from remote servers, * so we cannot tell if a remote client has that mode set. * And hacking it onto the end of the output of umode_str is EVIL BAD AND WRONG * (and breaks if the user is +r) so we won't do that either. */ if (strlen(umode_str(acptr)) < 1) strcpy(outbuf, " Umode(s):: <none>"); else ircd_snprintf(0, outbuf, sizeof(outbuf), " Umode(s):: +%s", umode_str(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (acptr->cli_user->joined == 0) send_reply(sptr, RPL_DATASTR, " Channel(s):: <none>"); else if (acptr->cli_user->joined > 50) { /* NB. As a sanity check, we DO NOT show the individual channels the * client is on if it is on > 50 channels. This is to prevent the ircd * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine * an Oper would want to see every single channel 'x' client is on anyway if * they are on *that* many). */ ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s):: - (total: %u)", acptr->cli_user->joined); send_reply(sptr, RPL_DATASTR, outbuf); } else { char chntext[BUFSIZE]; int len = strlen(" Channel(s):: "); int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name); *chntext = '\0'; strcpy(chntext, " Channel(s):: "); for (lp = acptr->cli_user->channel; lp; lp = lp->next_channel) { chptr = lp->channel; if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, RPL_DATASTR, chntext); *chntext = '\0'; strcpy(chntext, " Channel(s):: "); len = strlen(chntext); } if (IsDeaf(acptr)) *(chntext + len++) = '-'; if (is_chan_op(acptr, chptr)) *(chntext + len++) = '@'; if (is_half_op(acptr, chptr)) *(chntext + len++) = '%'; if (IsOper(sptr) && !ShowChannel(sptr,chptr)) *(chntext + len++) = '*'; if (IsZombie(lp)) *(chntext + len++) = '!'; if (len) *(chntext + len) = '\0'; strcpy(chntext + len, chptr->chname); len += strlen(chptr->chname); strcat(chntext + len, " "); len++; } if (chntext[0] != '\0') send_reply(sptr, RPL_DATASTR, chntext); } /* If client processing command ISN'T target (or a registered * Network Service), show idle time since the last time we * parsed something. */ if (MyUser(acptr) && !(IsService(acptr) == -1) && !(strCasediff(acptr->cli_name, sptr->cli_name) == 0)) { nowr = CurrentTime - acptr->cli_user->last; ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for:: %d days, %02ld:%02ld:%02ld", nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60); send_reply(sptr, RPL_DATASTR, outbuf); } /* Away message (if applicable) */ if (acptr->cli_user->away) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message:: %s", acptr->cli_user->away); send_reply(sptr, RPL_DATASTR, outbuf); } /* If local user.. */ if (MyUser(acptr)) { send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Ports:: %d -> %d (client -> server)", cli_port(acptr), cli_listener(acptr)->port); send_reply(sptr, RPL_DATASTR, outbuf); if (feature_bool(FEAT_CHECK_EXTENDED)) { /* Note: sendq = receiveq for a client (it makes sense really) */ ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent:: %u.%0.3u Kb (%u protocol messages)", cli_receiveK(acptr), cli_receiveB(acptr), cli_receiveM(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received:: %u.%0.3u Kb (%u protocol messages)", cli_sendK(acptr), cli_sendB(acptr), cli_sendM(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size:: %d bytes (max. %d bytes)", DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size:: %d bytes (max. %d bytes)", DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } /* Send 'END OF CHECK' message */ send_reply(sptr, RPL_ENDOFCHECK, " "); }
/* * 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; }
/** 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); }
/* * The function that actually prints out the WHO reply for a client found */ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, int fields, char* qrt) { char *p1; struct Channel *chptr = repchan; static char buf1[512]; /* NOTE: with current fields list and sizes this _cannot_ overrun, and also the message finally sent shouldn't ever be truncated */ p1 = buf1; buf1[1] = '\0'; /* If we don't have a channel and we need one... try to find it, unless the listing is for a channel service, we already know that there are no common channels, thus use PubChannel and not SeeChannel */ if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) && !IsChannelService(acptr)) { struct Membership* chan; for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel) if (PubChannel(chan->channel) && (acptr == sptr || !IsZombie(chan))) chptr = chan->channel; } /* Place the fields one by one in the buffer and send it note that fields == NULL means "default query" */ if (fields & WHO_FIELD_QTY) /* Query type */ { *(p1++) = ' '; if (BadPtr(qrt)) *(p1++) = '0'; else while ((*qrt) && (*(p1++) = *(qrt++))); } if (!fields || (fields & WHO_FIELD_CHA)) { char *p2; *(p1++) = ' '; if ((p2 = (chptr ? chptr->chname : NULL))) while ((*p2) && (*(p1++) = *(p2++))); else *(p1++) = '*'; } if (!fields || (fields & WHO_FIELD_UID)) { char *p2 = cli_user(acptr)->username; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_NIP) { const char* p2 = IsHiddenHost(acptr) && (!IsViewip(sptr) && acptr != sptr) ? feature_str(FEAT_HIDDEN_IP) : ircd_ntoa((const char*) &(cli_ip(acptr))); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_HOS)) { char *p2 = (IsViewip(sptr) || acptr == sptr) ? get_realhost(acptr) : get_virtualhost(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_SER)) { char *p2; int haspriv = IsAnOper(sptr) || es_representante(sptr); if (IsMe(cli_user(acptr)->server)) { if ((sptr != acptr) && feature_bool(FEAT_HIS_WHO_SERVERNAME) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(&me); } else { if (IsHiddenserv(cli_user(acptr)->server) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(cli_user(acptr)->server); } *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_NIC)) { char *p2 = cli_name(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_FLA)) { *(p1++) = ' '; if (cli_user(acptr)->away) *(p1++) = 'G'; else *(p1++) = 'H'; if ((IsAnOper(acptr) || es_representante(acptr) || IsPreoper(acptr))/* && (HasPriv(acptr, PRIV_DISPLAY) || HasPriv(sptr, PRIV_SEE_OPERS))*/) *(p1++) = '*'; if (fields) { /* If you specified flags then we assume you know how to parse * multiple channel status flags, as this is currently the only * way to know if someone has @'s *and* is +'d. */ if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; } else { if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; else if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; else if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; else if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; else if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; } if (IsDeaf(acptr)) *(p1++) = 'd'; if (IsAnOper(sptr)) { if (IsInvisible(acptr)) *(p1++) = 'i'; if (SendWallops(acptr)) *(p1++) = 'w'; if (SendDebug(acptr)) *(p1++) = 'g'; } if (HasHiddenHost(acptr)) *(p1++) = 'x'; if (IsAnOper(acptr)) *(p1++) = 'o'; if (IsPreoper(acptr)) *(p1++) = 'p'; if (IsHelpOp(acptr)) *(p1++) = 'h'; if (IsCoadmin(acptr)) *(p1++) = 'a'; if (IsAdmin(acptr)) *(p1++) = 'A'; if (IsDevel(acptr)) *(p1++) = 'D'; if (IsViewip(acptr)) *(p1++) = 'X'; } if (!fields || (fields & WHO_FIELD_DIS)) { *p1++ = ' '; if (!fields) *p1++ = ':'; /* Place colon here for default reply */ if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr)) *p1++ = (sptr == acptr) ? '0' : '3'; else /* three digit hopcount maximum */ p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr)); } if (fields & WHO_FIELD_IDL) { *p1++ = ' '; if (MyUser(acptr)) { p1 += ircd_snprintf(0, p1, 11, "%d", CurrentTime - cli_user(acptr)->last); } else { *p1++ = '0'; } } if (!fields || (fields & WHO_FIELD_REN)) { char *p2 = cli_info(acptr); *p1++ = ' '; if (fields) *p1++ = ':'; /* Place colon here for special reply */ while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_ACC) { char *p2 = cli_user(acptr)->account; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } /* The first char will always be an useless blank and we need to terminate buf1 */ *p1 = '\0'; p1 = buf1; send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1); }
/* * Send whois information for acptr to sptr */ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) { struct Client *a2cptr=0; struct Channel *chptr=0; int mlen; int len; static char buf[512]; const struct User* user = cli_user(acptr); const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr) && sptr != acptr ? &his : user->server; assert(user); send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, cli_info(acptr)); /* Display the channels this user is on. */ if (!IsChannelService(acptr)) { struct Membership* chan; mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name); len = 0; *buf = '\0'; for (chan = user->channel; chan; chan = chan->next_channel) { chptr = chan->channel; if (!ShowChannel(sptr, chptr) && !(IsOper(sptr) && IsLocalChannel(chptr->chname))) continue; if (acptr != sptr && IsZombie(chan)) continue; /* Don't show local channels when HIS is defined, unless it's a * remote WHOIS --ULtimaTe_ */ if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2) && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr)) continue; if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); *buf = '\0'; len = 0; } if (IsDeaf(acptr)) *(buf + len++) = '-'; if (!ShowChannel(sptr, chptr)) *(buf + len++) = '*'; if (IsDelayedJoin(chan) && (sptr != acptr)) *(buf + len++) = '<'; else if (IsChanOp(chan)) *(buf + len++) = '@'; else if (HasVoice(chan)) *(buf + len++) = '+'; else if (IsZombie(chan)) *(buf + len++) = '!'; if (len) *(buf + len) = '\0'; strcpy(buf + len, chptr->chname); len += strlen(chptr->chname); strcat(buf + len, " "); len++; } if (buf[0] != '\0') send_reply(sptr, RPL_WHOISCHANNELS, name, buf); } send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr), cli_info(a2cptr)); if (user) { if (user->away) send_reply(sptr, RPL_AWAY, name, user->away); if (SeeOper(sptr,acptr)) send_reply(sptr, RPL_WHOISOPERATOR, name); if (IsAccount(acptr)) send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, user->realhost, ircd_ntoa(&cli_ip(acptr))); /* Hint: if your looking to add more flags to a user, eg +h, here's * probably a good place to add them :) */ if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) || (sptr == acptr || IsAnOper(sptr) || parc >= 3))) send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, cli_firsttime(acptr)); } }
/** 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; }
/** 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; }
/** 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); 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 (s_state(&(con_socket(con))) == SS_CONNECTING) { completed_connection(cptr); /* for some reason, the os_get_sockerr() in completed_connect() * 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); } }
/** Searches for a K/G-line for a client. If one is found, notify the * user and disconnect them. * @param cptr Client to search for. * @return 0 if client is accepted; -1 if client was locally denied * (K-line); -2 if client was globally denied (G-line); -3 if client * was globally IP denied (Z-line). */ int find_kill(struct Client *cptr) { const char* host; const char* name; const char* realname; const char* country; const char* continent; const char* version; struct DenyConf* deny; struct Gline* agline = NULL; struct Zline* azline = NULL; assert(0 != cptr); if (!cli_user(cptr)) return 0; host = cli_sockhost(cptr); name = cli_user(cptr)->username; realname = cli_info(cptr); country = cli_countrycode(cptr); continent = cli_continentcode(cptr); version = cli_version(cptr); assert(strlen(host) <= HOSTLEN); assert((name ? strlen(name) : 0) <= HOSTLEN); assert((realname ? strlen(realname) : 0) <= REALLEN); assert((country ? strlen(country) : 0) <= 3); assert((continent ? strlen(continent) : 0) <= 3); assert((version ? strlen(version) : 0) <= VERSIONLEN); /* 2000-07-14: Rewrote this loop for massive speed increases. * -- Isomer */ if (!find_except_conf(cptr, EFLAG_KLINE)) { for (deny = denyConfList; deny; deny = deny->next) { if (deny->usermask && match(deny->usermask, name)) continue; if (deny->realmask && match(deny->realmask, realname)) continue; if (deny->countrymask && country && match(deny->countrymask, country)) continue; if (deny->continentmask && continent && match(deny->continentmask, continent)) continue; if (feature_bool(FEAT_CTCP_VERSIONING) && feature_bool(FEAT_CTCP_VERSIONING_KILL)) { if (deny->version && version && match(deny->version, version)) continue; } if (deny->bits > 0) { if (!ipmask_check(&cli_ip(cptr), &deny->address, deny->bits)) continue; } else if (deny->hostmask && match(deny->hostmask, host)) continue; if ((deny->flags & DENY_FLAGS_AUTHEX) && IsAccount(cptr)) { if (!EmptyString(deny->mark) && EmptyString(cli_killmark(cptr))) ircd_strncpy(cli_killmark(cptr), deny->mark, BUFSIZE); continue; } if (EmptyString(deny->message)) send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":Connection from your host is refused on this server."); else { if (deny->flags & DENY_FLAGS_FILE) killcomment(cptr, deny->message); else send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message); } return -1; } } /* Check Zlines here just in case a spoofed IP matches */ if (!feature_bool(FEAT_DISABLE_ZLINES) && (azline = zline_lookup(cptr, 0))) { /* * find active zlines * added a check against the user's IP address to find_zline() */ send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", ZlineReason(azline)); return -3; } /* Don't need to do an except lookup here as it's done in gline_lookup() */ if (!feature_bool(FEAT_DISABLE_GLINES) && (agline = gline_lookup(cptr, 0))) { /* * find active glines * added a check against the user's IP address to find_gline() -Kev */ send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline)); return -2; } 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; }