X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fclient.c;h=07d448fdbaf9a84873f32e22584636251e2d3fd5;hp=1edc7550e1f4e8c3ec9db65f952ca32d766ab241;hb=bf2eae3249cd7890c0189dfcf1a50b0e40e199b2;hpb=805bf0349064a1a1f6412c19a3a3bcb7fb7ccbe1 diff --git a/src/ngircd/client.c b/src/ngircd/client.c index 1edc7550..07d448fd 100644 --- a/src/ngircd/client.c +++ b/src/ngircd/client.c @@ -1,53 +1,45 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) + * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. - * - * Client management. */ - #define __client_c__ - #include "portab.h" -#include "imp.h" +/** + * @file + * Client management. + */ + #include #include #include #include #include #include +#include #include -#include "defines.h" #include "conn.h" - -#include "exp.h" -#include "client.h" - -#include #include "ngircd.h" #include "channel.h" -#include "resolve.h" #include "conf.h" +#include "conn-func.h" #include "hash.h" #include "irc-write.h" #include "log.h" +#include "match.h" #include "messages.h" -#include - - #define GETID_LEN (CLIENT_NICK_LEN-1) + 1 + (CLIENT_USER_LEN-1) + 1 + (CLIENT_HOST_LEN-1) + 1 - static CLIENT *This_Server, *My_Clients; static WHOWAS My_Whowas[MAX_WHOWAS]; @@ -62,6 +54,8 @@ static CLIENT *New_Client_Struct PARAMS(( void )); static void Generate_MyToken PARAMS(( CLIENT *Client )); static void Adjust_Counters PARAMS(( CLIENT *Client )); +static void Free_Client PARAMS(( CLIENT **Client )); + static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer, int Type, const char *ID, const char *User, const char *Hostname, const char *Info, @@ -71,6 +65,8 @@ static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer, static void Destroy_UserOrService PARAMS((CLIENT *Client,const char *Txt, const char *FwdMsg, bool SendQuit)); +static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix, + void *i)); GLOBAL void Client_Init( void ) @@ -85,7 +81,7 @@ Client_Init( void ) exit( 1 ); } - /* Client-Struktur dieses Servers */ + /* Client structure for this server */ This_Server->next = NULL; This_Server->type = CLIENT_SERVER; This_Server->conn_id = NONE; @@ -94,7 +90,7 @@ Client_Init( void ) This_Server->hops = 0; gethostname( This_Server->host, CLIENT_HOST_LEN ); - if (!Conf_NoDNS) { + if (Conf_DNS) { h = gethostbyname( This_Server->host ); if (h) strlcpy(This_Server->host, h->h_name, sizeof(This_Server->host)); } @@ -118,14 +114,15 @@ Client_Exit( void ) cnt = 0; c = My_Clients; - while( c ) - { + while(c) { cnt++; next = (CLIENT *)c->next; - free( c ); + Free_Client(&c); c = next; } - if( cnt ) Log( LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s" ); + if (cnt) + Log(LOG_INFO, "Freed %d client structure%s.", + cnt, cnt == 1 ? "" : "s"); } /* Client_Exit */ @@ -181,39 +178,47 @@ Client_NewRemoteUser(CLIENT *Introducer, const char *Nick, int Hops, const char */ static CLIENT * Init_New_Client(CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer, - int Type, const char *ID, const char *User, const char *Hostname, const char *Info, int Hops, - int Token, const char *Modes, bool Idented) + int Type, const char *ID, const char *User, const char *Hostname, + const char *Info, int Hops, int Token, const char *Modes, bool Idented) { CLIENT *client; - assert( Idx >= NONE ); - assert( Introducer != NULL ); - assert( Hostname != NULL ); + assert(Idx >= NONE); + assert(Introducer != NULL); - client = New_Client_Struct( ); - if( ! client ) return NULL; + client = New_Client_Struct(); + if (!client) + return NULL; client->starttime = time(NULL); client->conn_id = Idx; client->introducer = Introducer; client->topserver = TopServer; client->type = Type; - if( ID ) Client_SetID( client, ID ); - if( User ) Client_SetUser( client, User, Idented ); - if( Hostname ) Client_SetHostname( client, Hostname ); - if( Info ) Client_SetInfo( client, Info ); + if (ID) + Client_SetID(client, ID); + if (User) { + Client_SetUser(client, User, Idented); + Client_SetOrigUser(client, User); + } + if (Hostname) + Client_SetHostname(client, Hostname); + if (Info) + Client_SetInfo(client, Info); client->hops = Hops; client->token = Token; - if( Modes ) Client_SetModes( client, Modes ); - if( Type == CLIENT_SERVER ) Generate_MyToken( client ); + if (Modes) + Client_SetModes(client, Modes); + if (Type == CLIENT_SERVER) + Generate_MyToken(client); - if( strchr( client->modes, 'a' )) - strlcpy( client->away, DEFAULT_AWAY_MSG, sizeof( client->away )); + if (Client_HasMode(client, 'a')) + client->away = strndup(DEFAULT_AWAY_MSG, CLIENT_AWAY_LEN - 1); client->next = (POINTER *)My_Clients; My_Clients = client; - Adjust_Counters( client ); + Adjust_Counters(client); return client; } /* Init_New_Client */ @@ -225,14 +230,14 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen /* remove a client */ CLIENT *last, *c; - char msg[LINE_LEN]; + char msg[COMMAND_LEN]; const char *txt; assert( Client != NULL ); - if( LogMsg ) txt = LogMsg; - else txt = FwdMsg; - if( ! txt ) txt = "Reason unknown."; + txt = LogMsg ? LogMsg : FwdMsg; + if (!txt) + txt = "Reason unknown"; /* netsplit message */ if( Client->type == CLIENT_SERVER ) { @@ -272,10 +277,15 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen 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 ); + 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 */ @@ -287,17 +297,23 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen } 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 ); + 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 ); + Log(LOG_WARNING, + "Unregistered unknown client \"%s\": %s", + c->id[0] ? c->id : "(No Nick)", txt); } } - free( c ); + Free_Client(&c); break; } last = c; @@ -306,16 +322,60 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen } /* Client_Destroy */ +/** + * Set client hostname. + * + * If global hostname cloaking is in effect, don't set the real hostname + * but the configured one. + * + * @param Client The client of which the hostname should be set. + * @param Hostname The new hostname. + */ GLOBAL void Client_SetHostname( CLIENT *Client, const char *Hostname ) { - assert( Client != NULL ); - assert( Hostname != NULL ); + assert(Client != NULL); + assert(Hostname != NULL); + + if (Conf_CloakHost[0]) { + char cloak[GETID_LEN]; + + strlcpy(cloak, Hostname, GETID_LEN); + strlcat(cloak, Conf_CloakHostSalt, GETID_LEN); + snprintf(cloak, GETID_LEN, Conf_CloakHost, Hash(cloak)); - strlcpy( Client->host, Hostname, sizeof( Client->host )); + LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"", + Client_ID(Client), Client->host, cloak); + strlcpy(Client->host, cloak, sizeof(Client->host)); + } else { + LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"", + Client_ID(Client), Client->host, Hostname); + strlcpy(Client->host, Hostname, sizeof(Client->host)); + } } /* Client_SetHostname */ +/** + * Set IP address to display for a client. + * + * @param Client The client. + * @param IPAText Textual representation of the IP address or NULL to unset. + */ +GLOBAL void +Client_SetIPAText(CLIENT *Client, const char *IPAText) +{ + assert(Client != NULL); + + if (Client->ipa_text) + free(Client->ipa_text); + + if (*IPAText) + Client->ipa_text = strndup(IPAText, CLIENT_HOST_LEN - 1); + else + Client->ipa_text = NULL; +} + + GLOBAL void Client_SetID( CLIENT *Client, const char *ID ) { @@ -324,6 +384,11 @@ Client_SetID( CLIENT *Client, const char *ID ) strlcpy( Client->id, ID, sizeof( Client->id )); + if (Conf_CloakUserToNick) { + strlcpy( Client->user, ID, sizeof( Client->user )); + strlcpy( Client->info, ID, sizeof( Client->info )); + } + /* Hash */ Client->hash = Hash( Client->id ); } /* Client_SetID */ @@ -337,7 +402,9 @@ Client_SetUser( CLIENT *Client, const char *User, bool Idented ) assert( Client != NULL ); assert( User != NULL ); - if (Idented) { + if (Conf_CloakUserToNick) { + strlcpy(Client->user, Client->id, sizeof(Client->user)); + } else if (Idented) { strlcpy(Client->user, User, sizeof(Client->user)); } else { Client->user[0] = '~'; @@ -346,6 +413,26 @@ Client_SetUser( CLIENT *Client, const char *User, bool Idented ) } /* Client_SetUser */ +/** + * Set "original" user name of a client. + * This function saves the "original" user name, the user name specified by + * the peer using the USER command, into the CLIENT structure. This user + * name may be used for authentication, for example. + * @param Client The client. + * @param User User name to set. + */ +GLOBAL void +Client_SetOrigUser(CLIENT UNUSED *Client, const char UNUSED *User) +{ + assert(Client != NULL); + assert(User != NULL); + +#if defined(PAM) + strlcpy(Client->orig_user, User, sizeof(Client->orig_user)); +#endif +} /* Client_SetOrigUser */ + + GLOBAL void Client_SetInfo( CLIENT *Client, const char *Info ) { @@ -354,7 +441,10 @@ Client_SetInfo( CLIENT *Client, const char *Info ) assert( Client != NULL ); assert( Info != NULL ); - strlcpy(Client->info, Info, sizeof(Client->info)); + if (Conf_CloakUserToNick) + strlcpy(Client->info, Client->id, sizeof(Client->info)); + else + strlcpy(Client->info, Info, sizeof(Client->info)); } /* Client_SetInfo */ @@ -379,15 +469,19 @@ Client_SetFlags( CLIENT *Client, const char *Flags ) GLOBAL void -Client_SetPassword( CLIENT *Client, const char *Pwd ) +Client_SetAccountName(CLIENT *Client, const char *AccountName) { - /* set password sent by client */ + assert(Client != NULL); - assert( Client != NULL ); - assert( Pwd != NULL ); + if (Client->account_name) + free(Client->account_name); - strlcpy(Client->pwd, Pwd, sizeof(Client->pwd)); -} /* Client_SetPassword */ + if (*AccountName) + Client->account_name = strndup(AccountName, + CLIENT_NICK_LEN - 1); + else + Client->account_name = NULL; +} GLOBAL void @@ -398,7 +492,11 @@ Client_SetAway( CLIENT *Client, const char *Txt ) assert( Client != NULL ); assert( Txt != NULL ); - strlcpy( Client->away, Txt, sizeof( Client->away )); + if (Client->away) + free(Client->away); + + Client->away = strndup(Txt, CLIENT_AWAY_LEN - 1); + LogDebug("%s \"%s\" is away: %s", Client_TypeText(Client), Client_Mask(Client), Txt); } /* Client_SetAway */ @@ -439,19 +537,11 @@ Client_SetIntroducer( CLIENT *Client, CLIENT *Introducer ) } /* Client_SetIntroducer */ -GLOBAL void -Client_SetOperByMe( CLIENT *Client, bool OperByMe ) -{ - assert( Client != NULL ); - Client->oper_by_me = OperByMe; -} /* Client_SetOperByMe */ - - GLOBAL bool Client_ModeAdd( CLIENT *Client, char Mode ) { /* Set Mode. - * If Client already alread had Mode, return false. + * If Client already had Mode, return false. * If the Mode was newly set, return true. */ @@ -460,7 +550,7 @@ Client_ModeAdd( CLIENT *Client, char Mode ) assert( Client != NULL ); x[0] = Mode; x[1] = '\0'; - if (!strchr( Client->modes, x[0])) { + if (!Client_HasMode(Client, x[0])) { strlcat( Client->modes, x, sizeof( Client->modes )); return true; } @@ -495,13 +585,14 @@ Client_ModeDel( CLIENT *Client, char Mode ) } /* Client_ModeDel */ +/** + * Search CLIENT structure of a given nick name. + * + * @return Pointer to CLIENT structure or NULL if not found. + */ GLOBAL CLIENT * Client_Search( const char *Nick ) { - /* return Client-Structure that has the corresponding Nick. - * If none is found, return NULL. - */ - char search_id[CLIENT_ID_LEN], *ptr; CLIENT *c = NULL; UINT32 search_hash; @@ -522,20 +613,54 @@ Client_Search( const char *Nick ) c = (CLIENT *)c->next; } return NULL; -} /* Client_Search */ +} +/** + * Search first CLIENT structure matching a given mask of a server. + * + * The order of servers is arbitrary, but this function makes sure that the + * local server is always returned if the mask matches it. + * + * @return Pointer to CLIENT structure or NULL if no server could be found. + */ GLOBAL CLIENT * -Client_GetFromToken( CLIENT *Client, int Token ) +Client_SearchServer(const char *Mask) { - /* Client-Struktur, die den entsprechenden Introducer (=Client) - * und das gegebene Token hat, liefern. Wird keine gefunden, - * so wird NULL geliefert. */ + CLIENT *c; + + assert(Mask != NULL); + + /* First check if mask matches the local server */ + if (MatchCaseInsensitive(Mask, Client_ID(Client_ThisServer()))) + return Client_ThisServer(); + + c = My_Clients; + while (c) { + if (Client_Type(c) == CLIENT_SERVER) { + /* This is a server: check if Mask matches */ + if (MatchCaseInsensitive(Mask, c->id)) + return c; + } + c = (CLIENT *)c->next; + } + return NULL; +} + +/** + * Get client structure ("introducer") identfied by a server token. + * @return CLIENT structure or NULL if none could be found. + */ +GLOBAL CLIENT * +Client_GetFromToken( CLIENT *Client, int Token ) +{ CLIENT *c; assert( Client != NULL ); - assert( Token > 0 ); + + if (!Token) + return NULL; c = My_Clients; while (c) { @@ -595,21 +720,134 @@ Client_User( CLIENT *Client ) } /* Client_User */ +#ifdef PAM + +/** + * Get the "original" user name as supplied by the USER command. + * The user name as given by the client is used for authentication instead + * of the one detected using IDENT requests. + * @param Client The client. + * @return Original user name. + */ GLOBAL char * -Client_Hostname( CLIENT *Client ) +Client_OrigUser(CLIENT *Client) { + return Client->orig_user; +} /* Client_OrigUser */ + +#endif + +/** + * Return the hostname of a client. + * @param Client Pointer to client structure + * @return Pointer to client hostname + */ +GLOBAL char * +Client_Hostname(CLIENT *Client) { - assert( Client != NULL ); + assert (Client != NULL); return Client->host; -} /* Client_Hostname */ +} +/** + * Return the cloaked hostname of a client, if set. + * @param Client Pointer to the client structure. + * @return Pointer to the cloaked hostname or NULL if not set. + */ +GLOBAL char * +Client_HostnameCloaked(CLIENT *Client) +{ + assert(Client != NULL); + return Client->cloaked; +} +/** + * Get (potentially cloaked) hostname of a client to display it to other users. + * + * If the client has not enabled cloaking, the real hostname is used. + * + * @param Client Pointer to client structure + * @return Pointer to client hostname + */ GLOBAL char * -Client_Password( CLIENT *Client ) +Client_HostnameDisplayed(CLIENT *Client) { - assert( Client != NULL ); - return Client->pwd; -} /* Client_Password */ + assert(Client != NULL); + + /* Client isn't cloaked at all, return real hostname: */ + if (!Client_HasMode(Client, 'x')) + return Client_Hostname(Client); + + /* Use an already saved cloaked hostname, if there is one */ + if (Client->cloaked) + return Client->cloaked; + + Client_UpdateCloakedHostname(Client, NULL, NULL); + return Client->cloaked; +} + +GLOBAL const char * +Client_IPAText(CLIENT *Client) +{ + assert(Client != NULL); + + /* Not a local client? */ + if (Client_Conn(Client) <= NONE) + return "0.0.0.0"; + + if (!Client->ipa_text) + return Conn_GetIPAInfo(Client_Conn(Client)); + else + return Client->ipa_text; +} + +/** + * Update (and generate, if necessary) the cloaked hostname of a client. + * + * The newly set cloaked hostname is announced in the network using METADATA + * commands to peers that support this feature. + * + * @param Client The client of which the cloaked hostname should be updated. + * @param Origin The originator of the hostname change, or NULL if this server. + * @param Hostname The new cloaked hostname, or NULL if it should be generated. + */ +GLOBAL void +Client_UpdateCloakedHostname(CLIENT *Client, CLIENT *Origin, + const char *Hostname) +{ + char Cloak_Buffer[CLIENT_HOST_LEN]; + + assert(Client != NULL); + if (!Origin) + Origin = Client_ThisServer(); + + if (!Client->cloaked) { + Client->cloaked = malloc(CLIENT_HOST_LEN); + if (!Client->cloaked) + return; + } + if (!Hostname) { + /* Generate new cloaked hostname */ + if (*Conf_CloakHostModeX) { + strlcpy(Cloak_Buffer, Client->host, + sizeof(Cloak_Buffer)); + strlcat(Cloak_Buffer, Conf_CloakHostSalt, + sizeof(Cloak_Buffer)); + snprintf(Client->cloaked, CLIENT_HOST_LEN, + Conf_CloakHostModeX, Hash(Cloak_Buffer)); + } else + strlcpy(Client->cloaked, Client_ID(Client->introducer), + CLIENT_HOST_LEN); + } else + strlcpy(Client->cloaked, Hostname, CLIENT_HOST_LEN); + LogDebug("Cloaked hostname of \"%s\" updated to \"%s\"", + Client_ID(Client), Client->cloaked); + + /* Inform other servers in the network */ + IRC_WriteStrServersPrefixFlag(Client_NextHop(Origin), Origin, 'M', + "METADATA %s cloakhost :%s", + Client_ID(Client), Client->cloaked); +} GLOBAL char * Client_Modes( CLIENT *Client ) @@ -627,14 +865,6 @@ Client_Flags( CLIENT *Client ) } /* Client_Flags */ -GLOBAL bool -Client_OperByMe( CLIENT *Client ) -{ - assert( Client != NULL ); - return Client->oper_by_me; -} /* Client_OperByMe */ - - GLOBAL int Client_Hops( CLIENT *Client ) { @@ -675,23 +905,59 @@ Client_NextHop( CLIENT *Client ) /** - * return Client-ID ("client!user@host"), this ID is needed for e.g. - * prefixes. Returnes pointer to static buffer. + * Return ID of a client: "client!user@host" + * This client ID is used for IRC prefixes, for example. + * Please note that this function uses a global static buffer, so you can't + * nest invocations without overwriting earlier results! + * @param Client Pointer to client structure + * @return Pointer to global buffer containing the client ID */ GLOBAL char * Client_Mask( CLIENT *Client ) { - static char GetID_Buffer[GETID_LEN]; + static char Mask_Buffer[GETID_LEN]; - assert( Client != NULL ); + assert (Client != NULL); - if( Client->type == CLIENT_SERVER ) return Client->id; + /* Servers: return name only, there is no "mask" */ + if (Client->type == CLIENT_SERVER) + return Client->id; - snprintf(GetID_Buffer, GETID_LEN, "%s!%s@%s", Client->id, Client->user, Client->host); - return GetID_Buffer; + snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s", + Client->id, Client->user, Client->host); + return Mask_Buffer; } /* Client_Mask */ +/** + * Return ID of a client with cloaked hostname: "client!user@server-name" + * + * This client ID is used for IRC prefixes, for example. + * Please note that this function uses a global static buffer, so you can't + * nest invocations without overwriting earlier results! + * If the client has not enabled cloaking, the real hostname is used. + * + * @param Client Pointer to client structure + * @return Pointer to global buffer containing the client ID + */ +GLOBAL char * +Client_MaskCloaked(CLIENT *Client) +{ + static char Mask_Buffer[GETID_LEN]; + + assert (Client != NULL); + + /* Is the client using cloaking at all? */ + if (!Client_HasMode(Client, 'x')) + return Client_Mask(Client); + + snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s", Client->id, Client->user, + Client_HostnameDisplayed(Client)); + + return Mask_Buffer; +} /* Client_MaskCloaked */ + + GLOBAL CLIENT * Client_Introducer( CLIENT *Client ) { @@ -716,6 +982,14 @@ Client_HasMode( CLIENT *Client, char Mode ) } /* Client_HasMode */ +GLOBAL bool +Client_HasFlag( CLIENT *Client, char Flag ) +{ + assert( Client != NULL ); + return strchr( Client->flags, Flag ) != NULL; +} /* Client_HasFlag */ + + GLOBAL char * Client_Away( CLIENT *Client ) { @@ -724,23 +998,55 @@ Client_Away( CLIENT *Client ) } /* Client_Away */ -GLOBAL bool -Client_CheckNick( CLIENT *Client, char *Nick ) +GLOBAL char * +Client_AccountName(CLIENT *Client) { - assert( Client != NULL ); - assert( Nick != NULL ); + assert(Client != NULL); + return Client->account_name; +} - if (! Client_IsValidNick( Nick )) - { - IRC_WriteStrClient( Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID( Client ), Nick ); + +/** + * Make sure that a given nickname is valid. + * + * If the nickname is not valid for the given client, this function sends back + * the appropriate error messages. + * + * @param Client Client that wants to change the nickname. + * @param Nick New nickname. + * @returns true if nickname is valid, false otherwise. + */ +GLOBAL bool +Client_CheckNick(CLIENT *Client, char *Nick) +{ + assert(Client != NULL); + assert(Nick != NULL); + + if (!Client_IsValidNick(Nick)) { + if (strlen(Nick ) >= Conf_MaxNickLength) + IRC_WriteErrClient(Client, ERR_NICKNAMETOOLONG_MSG, + Client_ID(Client), Nick, + Conf_MaxNickLength - 1); + else + IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG, + Client_ID(Client), Nick); return false; } - /* Nick bereits vergeben? */ - if( Client_Search( Nick )) - { - /* den Nick gibt es bereits */ - IRC_WriteStrClient( Client, ERR_NICKNAMEINUSE_MSG, Client_ID( Client ), Nick ); + if (Client_Type(Client) != CLIENT_SERVER + && Client_Type(Client) != CLIENT_SERVICE) { + /* Make sure that this isn't a restricted/forbidden nickname */ + if (Conf_NickIsBlocked(Nick)) { + IRC_WriteErrClient(Client, ERR_FORBIDDENNICKNAME_MSG, + Client_ID(Client), Nick); + return false; + } + } + + /* Nickname already registered? */ + if (Client_Search(Nick)) { + IRC_WriteErrClient(Client, ERR_NICKNAMEINUSE_MSG, + Client_ID(Client), Nick); return false; } @@ -760,7 +1066,8 @@ Client_CheckID( CLIENT *Client, char *ID ) /* ID too long? */ if (strlen(ID) > CLIENT_ID_LEN) { - IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID(Client), ID); + IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG, + Client_ID(Client), ID); return false; } @@ -858,7 +1165,8 @@ Client_OperCount( void ) c = My_Clients; while( c ) { - if( c && ( c->type == CLIENT_USER ) && ( strchr( c->modes, 'o' ))) cnt++; + if (c && c->type == CLIENT_USER && Client_HasMode(c, 'o' )) + cnt++; c = (CLIENT *)c->next; } return cnt; @@ -896,23 +1204,31 @@ Client_MyMaxUserCount( void ) } /* Client_MyMaxUserCount */ +/** + * Check that a given nickname is valid. + * + * @param Nick the nickname to check. + * @returns true if nickname is valid, false otherwise. + */ GLOBAL bool -Client_IsValidNick( const char *Nick ) +Client_IsValidNick(const char *Nick) { const char *ptr; static const char goodchars[] = ";0123456789-"; - assert( Nick != NULL ); + assert (Nick != NULL); - if( Nick[0] == '#' ) return false; - if( strchr( goodchars, Nick[0] )) return false; - if( strlen( Nick ) >= Conf_MaxNickLength) return false; + if (strchr(goodchars, Nick[0])) + return false; + if (strlen(Nick ) >= Conf_MaxNickLength) + return false; ptr = Nick; - while( *ptr ) - { - if (( *ptr < 'A' ) && ( ! strchr( goodchars, *ptr ))) return false; - if ( *ptr > '}' ) return false; + while (*ptr) { + if (*ptr < 'A' && !strchr(goodchars, *ptr )) + return false; + if (*ptr > '}') + return false; ptr++; } @@ -952,6 +1268,79 @@ Client_StartTime(CLIENT *Client) } /* Client_Uptime */ +/** + * Reject a client when logging in. + * + * This function is called when a client isn't allowed to connect to this + * server. Possible reasons are bad server password, bad PAM password, + * or that the client is G/K-Line'd. + * + * After calling this function, the client isn't connected any more. + * + * @param Client The client to reject. + * @param Reason The reason why the client has been rejected. + * @param InformClient If true, send the exact reason to the client. + */ +GLOBAL void +Client_Reject(CLIENT *Client, const char *Reason, bool InformClient) +{ + char info[COMMAND_LEN]; + + assert(Client != NULL); + assert(Reason != NULL); + + if (InformClient) + snprintf(info, sizeof(info), "Access denied: %s", Reason); + else + strcpy(info, "Access denied: Bad password?"); + + Log(LOG_ERR, + "User \"%s\" rejected (connection %d): %s!", + Client_Mask(Client), Client_Conn(Client), Reason); + Conn_Close(Client_Conn(Client), Reason, info, true); +} + + +/** + * Introduce a new user or service client in the network. + * + * @param From Remote server introducing the client or NULL (local). + * @param Client New client. + * @param Type Type of the client (CLIENT_USER or CLIENT_SERVICE). + */ +GLOBAL void +Client_Introduce(CLIENT *From, CLIENT *Client, int Type) +{ + /* Set client type (user or service) */ + Client_SetType(Client, Type); + + if (From) { + if (Conf_NickIsService(Conf_GetServer(Client_Conn(From)), + Client_ID(Client))) + Client_SetType(Client, CLIENT_SERVICE); + LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).", + Client_TypeText(Client), Client_Mask(Client), + Client_Modes(Client), Client_ID(From), + Client_ID(Client_Introducer(Client)), + Client_Hops(Client), Client_Hops(Client) > 1 ? "s": ""); + } else { + Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).", + Client_TypeText(Client), Client_Mask(Client), + Client_Conn(Client)); + Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s", + Client_ID(Client), Client_User(Client), + Client_Hostname(Client), + Conn_IPA(Client_Conn(Client)), + Client_TypeText(Client)); + } + + /* Inform other servers */ + IRC_WriteStrServersPrefixFlag_CB(From, + From != NULL ? From : Client_ThisServer(), + '\0', cb_introduceClient, (void *)Client); +} /* Client_Introduce */ + + static unsigned long Count( CLIENT_TYPE Type ) { @@ -984,11 +1373,14 @@ MyCount( CLIENT_TYPE Type ) } /* MyCount */ +/** + * Allocate and initialize new CLIENT strcuture. + * + * @return Pointer to CLIENT structure or NULL on error. + */ static CLIENT * New_Client_Struct( void ) { - /* Neue CLIENT-Struktur pre-initialisieren */ - CLIENT *c; c = (CLIENT *)malloc( sizeof( CLIENT )); @@ -1002,14 +1394,34 @@ New_Client_Struct( void ) c->type = CLIENT_UNKNOWN; c->conn_id = NONE; - c->oper_by_me = false; c->hops = -1; c->token = -1; c->mytoken = -1; return c; -} /* New_Client */ +} +/** + * Free a CLIENT structure and its member variables. + */ +static void +Free_Client(CLIENT **Client) +{ + assert(Client != NULL); + assert(*Client != NULL); + + if ((*Client)->account_name) + free((*Client)->account_name); + if ((*Client)->away) + free((*Client)->away); + if ((*Client)->cloaked) + free((*Client)->cloaked); + if ((*Client)->ipa_text) + free((*Client)->ipa_text); + + free(*Client); + *Client = NULL; +} static void Generate_MyToken( CLIENT *Client ) @@ -1023,7 +1435,7 @@ Generate_MyToken( CLIENT *Client ) { if( c->mytoken == token ) { - /* Das Token wurde bereits vergeben */ + /* The token is already in use */ token++; c = My_Clients; continue; @@ -1069,6 +1481,10 @@ Client_RegisterWhowas( CLIENT *Client ) assert( Client != NULL ); + /* Don't register WHOWAS information when "MorePrivacy" is enabled. */ + if (Conf_MorePrivacy) + return; + now = time(NULL); /* Don't register clients that were connected less than 30 seconds. */ if( now - Client->starttime < 30 ) @@ -1086,7 +1502,7 @@ Client_RegisterWhowas( CLIENT *Client ) sizeof( My_Whowas[slot].id )); strlcpy( My_Whowas[slot].user, Client_User( Client ), sizeof( My_Whowas[slot].user )); - strlcpy( My_Whowas[slot].host, Client_Hostname( Client ), + strlcpy( My_Whowas[slot].host, Client_HostnameDisplayed( Client ), sizeof( My_Whowas[slot].host )); strlcpy( My_Whowas[slot].info, Client_Info( Client ), sizeof( My_Whowas[slot].info )); @@ -1097,7 +1513,7 @@ Client_RegisterWhowas( CLIENT *Client ) } /* Client_RegisterWhowas */ -GLOBAL char * +GLOBAL const char * Client_TypeText(CLIENT *Client) { assert(Client != NULL); @@ -1126,9 +1542,12 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool if(Client->conn_id != NONE) { /* Local (directly connected) client */ Log(LOG_NOTICE, - "%s \"%s\" unregistered (connection %d): %s", + "%s \"%s\" unregistered (connection %d): %s.", Client_TypeText(Client), Client_Mask(Client), Client->conn_id, Txt); + Log_ServerNotice('c', "Client exiting: %s (%s@%s) [%s]", + Client_ID(Client), Client_User(Client), + Client_Hostname(Client), Txt); if (SendQuit) { /* Inforam all the other servers */ @@ -1141,7 +1560,7 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool } } else { /* Remote client */ - LogDebug("%s \"%s\" unregistered: %s", + LogDebug("%s \"%s\" unregistered: %s.", Client_TypeText(Client), Client_Mask(Client), Txt); if(SendQuit) { @@ -1164,4 +1583,133 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool } /* Destroy_UserOrService */ +/** + * Introduce a new user or service client to a remote server. + * + * @param To The remote server to inform. + * @param Prefix Prefix for the generated commands. + * @param data CLIENT structure of the new client. + */ +static void +cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data) +{ + CLIENT *c = (CLIENT *)data; + + (void)Client_Announce(To, Prefix, c); + +} /* cb_introduceClient */ + + +/** + * Announce an user or service to a server. + * + * This function differentiates between RFC1459 and RFC2813 server links and + * generates the appropriate commands to register the user or service. + * + * @param Client Server + * @param Prefix Prefix for the generated commands + * @param User User to announce + */ +GLOBAL bool +Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User) +{ + CONN_ID conn; + char *modes, *user, *host; + + modes = Client_Modes(User); + user = Client_User(User) ? Client_User(User) : "-"; + host = Client_Hostname(User) ? Client_Hostname(User) : "-"; + + conn = Client_Conn(Client); + if (Conn_Options(conn) & CONN_RFC1459) { + /* RFC 1459 mode: separate NICK and USER commands */ + if (! Conn_WriteStr(conn, "NICK %s :%d", + Client_ID(User), Client_Hops(User) + 1)) + return DISCONNECTED; + if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s", + Client_ID(User), user, host, + Client_ID(Client_Introducer(User)), + Client_Info(User))) + return DISCONNECTED; + if (modes[0]) { + if (! Conn_WriteStr(conn, ":%s MODE %s +%s", + Client_ID(User), Client_ID(User), + modes)) + return DISCONNECTED; + } + } else { + /* RFC 2813 mode: one combined NICK or SERVICE command */ + if (Client_Type(User) == CLIENT_SERVICE + && Client_HasFlag(Client, 'S')) { + if (!IRC_WriteStrClientPrefix(Client, Prefix, + "SERVICE %s %d * +%s %d :%s", + Client_Mask(User), + Client_MyToken(Client_Introducer(User)), + modes, Client_Hops(User) + 1, + Client_Info(User))) + return DISCONNECTED; + } else { + if (!IRC_WriteStrClientPrefix(Client, Prefix, + "NICK %s %d %s %s %d +%s :%s", + Client_ID(User), Client_Hops(User) + 1, + user, host, + Client_MyToken(Client_Introducer(User)), + modes, Client_Info(User))) + return DISCONNECTED; + } + } + + if (Client_HasFlag(Client, 'M')) { + /* Synchronize metadata */ + if (Client_HostnameCloaked(User)) { + if (!IRC_WriteStrClientPrefix(Client, Prefix, + "METADATA %s cloakhost :%s", + Client_ID(User), + Client_HostnameCloaked(User))) + return DISCONNECTED; + } + + if (Client_AccountName(User)) { + if (!IRC_WriteStrClientPrefix(Client, Prefix, + "METADATA %s accountname :%s", + Client_ID(User), + Client_AccountName(User))) + return DISCONNECTED; + } + + if (Conn_GetCertFp(Client_Conn(User))) { + if (!IRC_WriteStrClientPrefix(Client, Prefix, + "METADATA %s certfp :%s", + Client_ID(User), + Conn_GetCertFp(Client_Conn(User)))) + return DISCONNECTED; + } + } + + return CONNECTED; +} /* Client_Announce */ + + +#ifdef DEBUG + +GLOBAL void +Client_DebugDump(void) +{ + CLIENT *c; + + Log(LOG_DEBUG, "Client status:"); + c = My_Clients; + while (c) { + Log(LOG_DEBUG, + " - %s: type=%d, host=%s, user=%s, conn=%d, start=%ld, flags=%s", + Client_ID(c), Client_Type(c), Client_Hostname(c), + Client_User(c), Client_Conn(c), Client_StartTime(c), + Client_Flags(c)); + c = (CLIENT *)c->next; + } +} /* Client_DumpClients */ + +#endif + + /* -eof- */