/* * dead_link - Adds client to a list of clients that need an exit_client() * */ void dead_link(struct Client *client_p) { dlink_node *m; const char *notice; if(IsClosing(client_p)) return; linebuf_donebuf(&client_p->localClient->buf_recvq); linebuf_donebuf(&client_p->localClient->buf_sendq); if(client_p->flags & FLAGS_SENDQEX) notice = "Max SendQ exceeded"; else notice = "Dead link"; if (!IsPerson(client_p) && !IsUnknown(client_p) && !IsClosing(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ADMIN, notice, get_client_name(client_p, HIDE_IP)); sendto_realops_flags(FLAGS_ALL, L_OPER, notice, get_client_name(client_p, MASK_IP)); } Debug((DEBUG_ERROR, notice, get_client_name(to, HIDE_IP))); assert(dlinkFind(&abort_list, client_p) == NULL); m = make_dlink_node(); dlinkAdd(client_p, m, &abort_list); SetDead(client_p); /* You are dead my friend */ }
/*! \brief Blindly opers up given source_p, using conf info. * All checks on passwords have already been done. * \param source_p Pointer to given client to oper * \param conf operator {} configuration record */ static void oper_up(struct Client *source_p, const struct MaskItem *conf) { const unsigned int old = source_p->umodes; ++Count.oper; SetOper(source_p); if (conf->modes) AddUMode(source_p, conf->modes); else if (ConfigGeneral.oper_umodes) AddUMode(source_p, ConfigGeneral.oper_umodes); if (!(old & UMODE_INVISIBLE) && HasUMode(source_p, UMODE_INVISIBLE)) ++Count.invisi; else if ((old & UMODE_INVISIBLE) && !HasUMode(source_p, UMODE_INVISIBLE)) --Count.invisi; assert(dlinkFind(&oper_list, source_p) == NULL); dlinkAdd(source_p, make_dlink_node(), &oper_list); AddOFlag(source_p, conf->port); if (HasOFlag(source_p, OPER_FLAG_ADMIN)) AddUMode(source_p, UMODE_ADMIN); if (!EmptyString(conf->whois)) { svstag_attach(&source_p->svstags, RPL_WHOISOPERATOR, "+", conf->whois); sendto_server(NULL, 0, 0, ":%s SVSTAG %s %ju %u + :%s", me.id, source_p->id, source_p->tsinfo, RPL_WHOISOPERATOR, conf->whois); } ilog(LOG_TYPE_OPER, "OPER %s by %s!%s@%s", conf->name, source_p->name, source_p->username, source_p->host); sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE, "%s is now an operator", get_oper_name(source_p)); sendto_server(NULL, 0, 0, ":%s GLOBOPS :%s is now an operator", me.id, get_oper_name(source_p)); send_umode_out(source_p, old); sendto_one_numeric(source_p, &me, RPL_YOUREOPER); }
/* ************************************************************************ */ int _BlockHeapFree(BlockHeap * bh, void *ptr) { Block *block; struct MemBlock *memblock; assert(bh != NULL); assert(ptr != NULL); if (bh == NULL) { ilog(L_NOTICE, "balloc.c:BlockHeapFree() bh == NULL"); return(1); } if (ptr == NULL) { ilog(L_NOTICE, "balloc.BlockHeapFree() ptr == NULL"); return(1); } memblock = (void *)((size_t)ptr - sizeof(MemBlock)); assert(memblock->block != NULL); if(memblock->block == NULL) { outofmemory(); } /* Is this block really on the used list? */ assert(dlinkFind(&memblock->block->used_list, memblock) == NULL); block = memblock->block; bh->freeElems++; block->freeElems++; mem_frob(ptr, bh->elemSize); dlinkDelete(&memblock->self, &block->used_list); dlinkAdd(ptr, &memblock->self, &block->free_list); return(0); }
/* ** mo_jupe ** parv[0] = sender prefix ** parv[1] = server we're juping ** parv[2] = reason for jupe */ static void mo_jupe(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p; struct Client *ajupe; dlink_node *m; char reason[REALLEN+2]; if(!ServerInfo.hub) return; if(!IsOperAdmin(source_p)) { sendto_one(source_p, ":%s NOTICE %s :You must be an admin to use this command", me.name, parv[0]); return; } if (bogus_host(parv[1])) { sendto_one(source_p, ":%s NOTICE %s :Invalid servername: %s", me.name, parv[0], parv[1]); return; } if(match(parv[1], me.name)) { sendto_one(source_p, ":%s NOTICE %s :I cant jupe myself!", me.name, source_p->name); return; } sendto_wallops_flags(UMODE_WALLOP, &me, "JUPE for %s requested by %s: %s", parv[1], get_oper_name(source_p), parv[2]); sendto_server(NULL, NOCAPS, NOCAPS, ":%s WALLOPS :JUPE for %s requested by %s!%s@%s: %s", parv[0], parv[1], source_p->name, source_p->username, source_p->host, parv[2]); ilog(L_NOTICE, "JUPE for %s requested by %s: %s", parv[1], get_oper_name(source_p), parv[2]); target_p= find_server(parv[1]); if(target_p) exit_client(client_p, target_p, &me, parv[2]); sendto_server(NULL, NOCAPS, NOCAPS, ":%s SERVER %s 1 :JUPED: %s", me.name, parv[1], parv[2]); sendto_realops_flags(UMODE_ALL, L_ALL, "Link with %s established: (JUPED) link", parv[1]); ajupe = make_client(NULL); /* make_client() adds client to unknown_list */ m = dlinkFind(&unknown_list, ajupe); if(m != NULL) dlinkDelete(m, &unknown_list); free_dlink_node(m); make_server(ajupe); ajupe->hopcount = 1; strlcpy(ajupe->name,parv[1],HOSTLEN); /* we need to give 7 chars to prepend "JUPED: " */ if(strlen(parv[2]) > (REALLEN-7)) parv[2][REALLEN-7] = '\0'; ircsprintf(reason, "%s %s", "JUPED:", parv[2]); strlcpy(ajupe->info,reason,REALLEN); ajupe->serv->up = me.name; ajupe->servptr = &me; SetServer(ajupe); SetDead(ajupe); Count.server++; Count.myserver++; /* Some day, all these lists will be consolidated *sigh* */ add_client_to_list(ajupe); add_to_client_hash_table(ajupe->name, ajupe); dlinkAdd(ajupe, &ajupe->lnode, &ajupe->servptr->serv->servers); add_server_to_list(ajupe); }
/* ** Exit one client, local or remote. Assuming all dependents have ** been already removed, and socket closed for local client. */ static void exit_one_client(struct Client *client_p, struct Client *source_p, struct Client *from, const char *comment) { struct Client* target_p; dlink_node *lp; dlink_node *next_lp; if (IsServer(source_p)) { if (source_p->servptr && source_p->servptr->serv) del_client_from_llist(&(source_p->servptr->serv->servers), source_p); else ts_warn("server %s without servptr!", source_p->name); if(!IsMe(source_p)) remove_server_from_list(source_p); } else if (source_p->servptr && source_p->servptr->serv) { del_client_from_llist(&(source_p->servptr->serv->users), source_p); } /* there are clients w/o a servptr: unregistered ones */ /* ** For a server or user quitting, propogate the information to ** other servers (except to the one where is came from (client_p)) */ if (IsMe(source_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(source_p)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ /* ** The bulk of this is done in remove_dependents now, all ** we have left to do is send the SQUIT upstream. -orabidoo */ if (source_p->localClient) { if(source_p->localClient->ctrlfd > -1) { fd_close(source_p->localClient->ctrlfd); source_p->localClient->ctrlfd = -1; #ifndef HAVE_SOCKETPAIR fd_close(source_p->localClient->ctrlfd_r); fd_close(source_p->localClient->fd_r); source_p->localClient->ctrlfd_r = -1; source_p->localClient->fd_r = -1; #endif } } target_p = source_p->from; if (target_p && IsServer(target_p) && target_p != client_p && !IsMe(target_p) && (source_p->flags & FLAGS_KILLED) == 0) sendto_one(target_p, ":%s SQUIT %s :%s", from->name, source_p->name, comment); } else if (source_p->name[0]) /* ...just clean all others with QUIT... */ { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((source_p->flags & FLAGS_KILLED) == 0) { sendto_server(client_p, source_p, NULL, NOCAPS, NOCAPS, NOFLAGS, ":%s QUIT :%s", source_p->name, comment); } /* ** 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*) */ if (source_p->user) { sendto_common_channels_local(source_p, ":%s!%s@%s QUIT :%s", source_p->name, source_p->username, source_p->host, comment); for (lp = source_p->user->channel.head; lp; lp = next_lp) { next_lp = lp->next; remove_user_from_channel(lp->data, source_p); } /* Should not be in any channels now */ assert(source_p->user->channel.head == NULL); /* Clean up invitefield */ for (lp = source_p->user->invited.head; lp; lp = next_lp) { next_lp = lp->next; del_invite(lp->data, source_p); } /* Clean up allow lists */ del_all_accepts(source_p); add_history(source_p, 0); off_history(source_p); if (HasID(source_p)) del_from_id_hash_table(source_p->user->id, source_p); /* again, this is all that is needed */ } } /* * Remove source_p from the client lists */ del_from_client_hash_table(source_p->name, source_p); /* remove from global client list */ remove_client_from_list(source_p); /* Check to see if the client isn't already on the dead list */ assert(dlinkFind(&dead_list, source_p) == NULL); /* add to dead client dlist */ lp = make_dlink_node(); SetDead(source_p); dlinkAdd(source_p, lp, &dead_list); }
/* ** exit_client - This is old "m_bye". Name changed, because this is not a ** protocol function, but a general server utility function. ** ** This function exits a client of *any* type (user, server, etc) ** from this server. Also, this generates all necessary prototol ** messages that this exit may cause. ** ** 1) If the client is a local client, then this implicitly ** exits all other clients depending on this connection (e.g. ** remote clients having 'from'-field that points to this. ** ** 2) If the client is a remote client, then only this is exited. ** ** For convenience, this function returns a suitable value for ** m_function return value: ** ** CLIENT_EXITED if (client_p == source_p) ** 0 if (client_p != source_p) */ int exit_client( struct Client* client_p, /* The local client originating the * exit or NULL, if this exit is * generated by this server for * internal reasons. * This will not get any of the * generated messages. */ struct Client* source_p, /* Client exiting */ struct Client* from, /* Client firing off this Exit, * never NULL! */ const char* comment /* Reason for the exit */ ) { char comment1[HOSTLEN + HOSTLEN + 2]; dlink_node *m; fprintf(stderr, "Exiting client: %s\n", comment); if (MyConnect(source_p)) { /* DO NOT REMOVE. exit_client can be called twice after a failed * read/write. */ if(IsClosing(source_p)) return 0; SetClosing(source_p); if (source_p->flags & FLAGS_IPHASH) remove_one_ip(&source_p->localClient->ip); delete_adns_queries(source_p->localClient->dns_query); delete_identd_queries(source_p); client_flush_input(source_p); /* This source_p could have status of one of STAT_UNKNOWN, STAT_CONNECTING * STAT_HANDSHAKE or STAT_UNKNOWN * all of which are lumped together into unknown_list * * In all above cases IsRegistered() will not be true. */ if (!IsRegistered(source_p)) { m = dlinkFind(&unknown_list,source_p); if(m != NULL) { dlinkDelete(m, &unknown_list); free_dlink_node(m); } } if (IsOper(source_p)) { m = dlinkFind(&oper_list,source_p); if(m != NULL) { dlinkDelete(m, &oper_list); free_dlink_node(m); } } if (IsClient(source_p)) { Count.local--; if(IsPerson(source_p)) /* a little extra paranoia */ { m = dlinkFind(&lclient_list,source_p); if(m != NULL) { dlinkDelete(m,&lclient_list); free_dlink_node(m); } } } /* As soon as a client is known to be a server of some sort * it has to be put on the serv_list, or SJOIN's to this new server * from the connect burst will not be seen. */ if (IsServer(source_p) || IsConnecting(source_p) || IsHandshake(source_p)) { m = dlinkFind(&serv_list,source_p); if(m != NULL) { dlinkDelete(m,&serv_list); free_dlink_node(m); unset_chcap_usage_counts(source_p); } } if (IsServer(source_p)) { Count.myserver--; if(ServerInfo.hub) remove_lazylink_flags(source_p->localClient->serverMask); else uplink = NULL; } if (IsPerson(source_p)) sendto_realops_flags(FLAGS_CCONN, L_ALL, "Client exiting: %s (%s@%s) [%s] [%s]", source_p->name, source_p->username, source_p->host, comment, source_p->localClient->sockhost); log_user_exit(source_p); if (source_p->localClient->fd >= 0) { if (client_p != NULL && source_p != client_p) sendto_one(source_p, "ERROR :Closing Link: %s %s (%s)", source_p->host, source_p->name, comment); else sendto_one(source_p, "ERROR :Closing Link: %s (%s)", source_p->host, comment); } /* ** Currently only server connections can have ** depending remote clients here, but it does no ** harm to check for all local clients. In ** future some other clients than servers might ** have remotes too... ** ** Close the Client connection first and mark it ** so that no messages are attempted to send to it. ** (The following *must* make MyConnect(source_p) == FALSE!). ** It also makes source_p->from == NULL, thus it's unnecessary ** to test whether "source_p != target_p" in the following loops. */ close_connection(source_p); } if(IsServer(source_p)) { if(ConfigServerHide.hide_servers) { /* set netsplit message to "me.name *.split" to still show * that its a split, but hide the servers splitting */ ircsprintf(comment1,"%s *.split", me.name); } else { if((source_p->serv) && (source_p->serv->up)) strcpy(comment1, source_p->serv->up); else strcpy(comment1, "<Unknown>"); strcat(comment1," "); strcat(comment1, source_p->name); } remove_dependents(client_p, source_p, from, comment, comment1); if (source_p->servptr == &me) { sendto_realops_flags(FLAGS_ALL, L_ALL, "%s was connected for %d seconds. %d/%d sendK/recvK.", source_p->name, (int)(CurrentTime - source_p->firsttime), source_p->localClient->sendK, source_p->localClient->receiveK); ilog(L_NOTICE, "%s was connected for %d seconds. %d/%d sendK/recvK.", source_p->name, CurrentTime - source_p->firsttime, source_p->localClient->sendK, source_p->localClient->receiveK); } } exit_one_client(client_p, source_p, from, comment); return client_p == source_p ? CLIENT_EXITED : 0; }