/** * Kill an client identified by its nick name. * * Please note that after killig a client, its CLIENT cond CONNECTION * structures are invalid. So the caller must make sure on its own not to * access data of probably killed clients after calling this function! * * @param Client The client from which the command leading to the KILL has * been received, or NULL. The KILL will no be forwarded in this * direction. Only relevant when From is set, too. * @param From The client from which the command originated, or NULL for the local server. * @param Nick The nick name to kill. * @param Reason Text to send as reason to the client and other servers. */ GLOBAL bool IRC_KillClient(CLIENT *Client, CLIENT *From, const char *Nick, const char *Reason) { const char *msg; CONN_ID my_conn, conn; CLIENT *c; /* Do we know such a client in the network? */ c = Client_Search(Nick); if (!c) { LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick); return CONNECTED; } /* Inform other servers */ IRC_WriteStrServersPrefix(From ? Client : NULL, From ? From : Client_ThisServer(), "KILL %s :%s", Nick, Reason); if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK) { /* Target of this KILL is not a regular user, this is * invalid! So we ignore this case if we received a * regular KILL from the network and try to kill the * client/connection anyway (but log an error!) if the * origin is the local server. */ if (Client != Client_ThisServer()) { /* Invalid KILL received from remote */ if (Client_Type(c) == CLIENT_SERVER) msg = ERR_CANTKILLSERVER_MSG; else msg = ERR_NOPRIVILEGES_MSG; return IRC_WriteErrClient(Client, msg, Client_ID(Client)); } Log(LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!", Client_Type(c), Nick); } /* Save ID of this connection */ my_conn = Client_Conn(Client); /* Kill the client NOW: * - Close the local connection (if there is one), * - Destroy the CLIENT structure for remote clients. * Note: Conn_Close() removes the CLIENT structure as well. */ conn = Client_Conn(c); if(conn > NONE) Conn_Close(conn, NULL, Reason, true); else Client_Destroy(c, NULL, Reason, false); /* Are we still connected or were we killed, too? */ if (my_conn > NONE && Conn_GetClient(my_conn)) return CONNECTED; else return DISCONNECTED; }
GLOBAL void Client_Exit( void ) { CLIENT *c, *next; int cnt; if( NGIRCd_SignalRestart ) Client_Destroy( This_Server, "Server going down (restarting).", NULL, false ); else Client_Destroy( This_Server, "Server going down.", NULL, false ); cnt = 0; c = My_Clients; while(c) { cnt++; next = (CLIENT *)c->next; Free_Client(&c); c = next; } if (cnt) Log(LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s"); } /* Client_Exit */
/** * Handler for the IRC "QUIT" command. * * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit". * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @returns CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_QUIT( CLIENT *Client, REQUEST *Req ) { CLIENT *target; char quitmsg[LINE_LEN]; assert(Client != NULL); assert(Req != NULL); /* Wrong number of arguments? */ if (Req->argc > 1) return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); if (Req->argc == 1) strlcpy(quitmsg, Req->argv[0], sizeof quitmsg); if (Client_Type(Client) == CLIENT_SERVER) { /* Server */ target = Client_Search(Req->prefix); if (!target) { Log(LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID(Client)); return CONNECTED; } if (target != Client) { Client_Destroy(target, "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true); return CONNECTED; } else { Conn_Close(Client_Conn(Client), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true); return DISCONNECTED; } } else { if (Req->argc == 1 && quitmsg[0] != '\"') { /* " " to avoid confusion */ strlcpy(quitmsg, "\"", sizeof quitmsg); strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1); strlcat(quitmsg, "\"", sizeof quitmsg ); } /* User, Service, or not yet registered */ Conn_Close(Client_Conn(Client), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true); return DISCONNECTED; } } /* IRC_QUIT */
/** * Handler for the IRC "ERROR" command. * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_ERROR(CLIENT *Client, REQUEST *Req) { char *msg; assert( Client != NULL ); assert( Req != NULL ); if (Client_Type(Client) != CLIENT_GOTPASS && Client_Type(Client) != CLIENT_GOTPASS_2813 && Client_Type(Client) != CLIENT_UNKNOWNSERVER && Client_Type(Client) != CLIENT_SERVER && Client_Type(Client) != CLIENT_SERVICE) { LogDebug("Ignored ERROR command from \"%s\" ...", Client_Mask(Client)); IRC_SetPenalty(Client, 2); return CONNECTED; } if (Req->argc < 1) { msg = "Got ERROR command"; Log(LOG_NOTICE, "Got ERROR from \"%s\"!", Client_Mask(Client)); } else { msg = Req->argv[0]; Log(LOG_NOTICE, "Got ERROR from \"%s\": \"%s\"!", Client_Mask(Client), msg); } if (Client_Conn(Client) != NONE) { Client_Destroy(Client, NULL, msg, false); return DISCONNECTED; } return CONNECTED; } /* IRC_ERROR */
/** * Handler for the IRC "KILL" command. * * This function implements the IRC command "KILL" wich is used to selectively * disconnect clients. It can be used by IRC operators and servers, for example * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1. * * Please note that this function is also called internally, without a real * KILL command being received over the network! Client is Client_ThisServer() * in this case, and the prefix in Req is NULL. * * @param Client The client from which this command has been received * or Client_ThisServer() when generated interanlly. * @param Req Request structure with prefix and all parameters. * @returns CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_KILL( CLIENT *Client, REQUEST *Req ) { CLIENT *prefix, *c; char reason[COMMAND_LEN], *msg; CONN_ID my_conn, conn; assert (Client != NULL); assert (Req != NULL); if (Client_Type(Client) != CLIENT_SERVER && !Client_OperByMe(Client)) return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG, Client_ID(Client)); if (Req->argc != 2) return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); /* Get prefix (origin); use the client if no prefix is given. */ if (Req->prefix) prefix = Client_Search(Req->prefix); else prefix = Client; /* Log a warning message and use this server as origin when the * prefix (origin) is invalid. */ if (!prefix) { Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!", Req->prefix ); prefix = Client_ThisServer(); } if (Client != Client_ThisServer()) Log(LOG_NOTICE|LOG_snotice, "Got KILL command from \"%s\" for \"%s\": %s", Client_Mask(prefix), Req->argv[0], Req->argv[1]); /* Build reason string: Prefix the "reason" if the originator is a * regular user, so users can't spoof KILLs of servers. */ if (Client_Type(Client) == CLIENT_USER) snprintf(reason, sizeof(reason), "KILLed by %s: %s", Client_ID(Client), Req->argv[1]); else strlcpy(reason, Req->argv[1], sizeof(reason)); /* Inform other servers */ IRC_WriteStrServersPrefix(Client, prefix, "KILL %s :%s", Req->argv[0], reason); /* Save ID of this connection */ my_conn = Client_Conn( Client ); /* Do we host such a client? */ c = Client_Search( Req->argv[0] ); if( c ) { if(( Client_Type( c ) != CLIENT_USER ) && ( Client_Type( c ) != CLIENT_GOTNICK )) { /* Target of this KILL is not a regular user, this is * invalid! So we ignore this case if we received a * regular KILL from the network and try to kill the * client/connection anyway (but log an error!) if the * origin is the local server. */ if( Client != Client_ThisServer( )) { /* Invalid KILL received from remote */ if( Client_Type( c ) == CLIENT_SERVER ) msg = ERR_CANTKILLSERVER_MSG; else msg = ERR_NOPRIVILEGES_MSG; return IRC_WriteStrClient( Client, msg, Client_ID( Client )); } Log( LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!", Client_Type( c ), Req->argv[0] ); } /* Kill the client NOW: * - Close the local connection (if there is one), * - Destroy the CLIENT structure for remote clients. * Note: Conn_Close() removes the CLIENT structure as well. */ conn = Client_Conn( c ); if(conn > NONE) Conn_Close(conn, NULL, reason, true); else Client_Destroy(c, NULL, reason, false); } else Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] ); /* Are we still connected or were we killed, too? */ if(( my_conn > NONE ) && ( Conn_GetClient( my_conn ))) return CONNECTED; else return DISCONNECTED; } /* IRC_KILL */
GLOBAL void Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool SendQuit ) { /* remove a client */ CLIENT *last, *c; char msg[COMMAND_LEN]; const char *txt; assert( Client != NULL ); txt = LogMsg ? LogMsg : FwdMsg; if (!txt) txt = "Reason unknown"; /* netsplit message */ if( Client->type == CLIENT_SERVER ) { strlcpy(msg, This_Server->id, sizeof (msg)); strlcat(msg, " ", sizeof (msg)); strlcat(msg, Client->id, sizeof (msg)); } last = NULL; c = My_Clients; while( c ) { if(( Client->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c != Client )) { /* * The client that is about to be removed is a server, * the client we are checking right now is a child of that * server and thus has to be removed, too. * * Call Client_Destroy() recursively with the server as the * new "object to be removed". This starts the cycle again, until * all servers that are linked via the original server have been * removed. */ Client_Destroy( c, NULL, msg, false ); last = NULL; c = My_Clients; continue; } if( c == Client ) { /* found the client: remove it */ if( last ) last->next = c->next; else My_Clients = (CLIENT *)c->next; if(c->type == CLIENT_USER || c->type == CLIENT_SERVICE) Destroy_UserOrService(c, txt, FwdMsg, SendQuit); else if( c->type == CLIENT_SERVER ) { if (c != This_Server) { if (c->conn_id != NONE) Log(LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered (connection %d): %s.", c->id, c->conn_id, txt); else Log(LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered: %s.", c->id, txt); } /* inform other servers */ if( ! NGIRCd_SignalQuit ) { if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :%s", c->id, FwdMsg ); else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :", c->id ); } } else { if (c->conn_id != NONE) { if (c->id[0]) Log(LOG_NOTICE, "Client \"%s\" unregistered (connection %d): %s.", c->id, c->conn_id, txt); else Log(LOG_NOTICE, "Client unregistered (connection %d): %s.", c->conn_id, txt); } else { Log(LOG_WARNING, "Unregistered unknown client \"%s\": %s", c->id[0] ? c->id : "(No Nick)", txt); } } Free_Client(&c); break; } last = c; c = (CLIENT *)c->next; } } /* Client_Destroy */
void Dht_DestroyClient(void *client) { Client_Destroy((Client *)client); }
/** * Handler for the IRC command "SQUIT". * See RFC 2813 section 4.1.2 and RFC 2812 section 3.1.8. */ GLOBAL bool IRC_SQUIT(CLIENT * Client, REQUEST * Req) { char msg[COMMAND_LEN], logmsg[COMMAND_LEN]; CLIENT *from, *target; CONN_ID con; int loglevel; assert(Client != NULL); assert(Req != NULL); if (Client_Type(Client) != CLIENT_SERVER && !Client_HasMode(Client, 'o')) return Op_NoPrivileges(Client, Req); /* Bad number of arguments? */ if (Req->argc != 2) return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) { from = Client_Search(Req->prefix); if (Client_Type(from) != CLIENT_SERVER && !Op_Check(Client, Req)) return Op_NoPrivileges(Client, Req); } else from = Client; if (!from) return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix); if (Client_Type(Client) == CLIENT_USER) loglevel = LOG_NOTICE | LOG_snotice; else loglevel = LOG_DEBUG; Log(loglevel, "Got SQUIT from %s for \"%s\": \"%s\" ...", Client_ID(from), Req->argv[0], Req->argv[1]); target = Client_Search(Req->argv[0]); if (Client_Type(Client) != CLIENT_SERVER && target == Client_ThisServer()) return Op_NoPrivileges(Client, Req); if (!target) { /* The server is (already) unknown */ Log(LOG_WARNING, "Got SQUIT from %s for unknown server \"%s\"!?", Client_ID(Client), Req->argv[0]); return CONNECTED; } con = Client_Conn(target); if (Req->argv[1][0]) if (Client_NextHop(from) != Client || con > NONE) snprintf(msg, sizeof(msg), "%s (SQUIT from %s)", Req->argv[1], Client_ID(from)); else strlcpy(msg, Req->argv[1], sizeof(msg)); else snprintf(msg, sizeof(msg), "Got SQUIT from %s", Client_ID(from)); if (con > NONE) { /* We are directly connected to the target server, so we * have to tear down the connection and to inform all the * other remaining servers in the network */ IRC_SendWallops(Client_ThisServer(), Client_ThisServer(), "Received SQUIT %s from %s: %s", Req->argv[0], Client_ID(from), Req->argv[1][0] ? Req->argv[1] : "-"); Conn_Close(con, NULL, msg, true); if (con == Client_Conn(Client)) return DISCONNECTED; } else { /* This server is not directly connected, so the SQUIT must * be forwarded ... */ if (Client_Type(from) != CLIENT_SERVER) { /* The origin is not an IRC server, so don't evaluate * this SQUIT but simply forward it */ IRC_WriteStrClientPrefix(Client_NextHop(target), from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]); } else { /* SQUIT has been generated by another server, so * remove the target server from the network! */ logmsg[0] = '\0'; if (!strchr(msg, '(')) snprintf(logmsg, sizeof(logmsg), "%s (SQUIT from %s)", Req->argv[1], Client_ID(from)); Client_Destroy(target, logmsg[0] ? logmsg : msg, msg, false); } } return CONNECTED; } /* IRC_SQUIT */
void server_exec(Socket server, SockAddrIn server_addr, ClientSet* clients, NoticeBoard* board) { int i; if (listen(server, 5) == -1) { error("Could not listen on socket"); exit(1); } while (running) { usleep(100000); /* -- Accepting connections -- */ socklen_t sz = sizeof(server_addr); Socket new_socket = accept(server, (SockAddr*)&server_addr, &sz); /* Error handling, most likely EAGAIN since we're using * non blocking sockets. */ if (new_socket == -1) { if (errno != EAGAIN) { error("Could not accept connection"); } } else { int j = 0; fcntl(new_socket, F_SETFL, O_NONBLOCK); Client* c = ClientSet_Add(clients, new_socket); if (c == NULL) continue; memset(c->properties.nick, 0, NICK_MAXLEN); strcpy(c->properties.nick, "user"); for (i = 0; i < clients->size; i++) { if (&clients->clients[i] == c || clients->clients[i].state == DISCONNECTED) continue; if (!strcmp(clients->clients[i].properties.nick, c->properties.nick)) { j++; sprintf(c->properties.nick, "user(%d)", j); i = 0; } } SockAddr peer_address; socklen_t addrlen = sizeof(peer_address); int has_error = getpeername(new_socket, &peer_address, &addrlen); if (has_error == -1) { error("Error getting peer name"); } SockAddrIn *addr_in = (struct sockaddr_in *)&peer_address; c->ip_string = ipaddr(*addr_in); printf("Client connected (%s)\n", c->ip_string); send_motd(c); } /* -- Reading data -- */ for (i = 0; i < clients->size; i++) { if (clients->clients[i].state == DISCONNECTED) { Client_Destroy(&clients->clients[i]); continue; } client_exec(&(clients->clients[i]), i, clients, board); } } }
void disconnect(Client* client) { printf("Client disconnected (%s)\n", client->ip_string); Client_Destroy(client); }