/*
* ----------------------------------------------------------------
* Night Light IRC Proxy - Client Handle Functions
* ----------------------------------------------------------------
* Copyright (C) 1997-2007 Jonas Kvinge <jonas@night-light.net>
* All rights reserved.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Last modified by:
* Jonas Kvinge (15.11.2006)
*
*/
#define CLIENT_HANDLE_C
#define NEED_SYS_TYPES_H 1 /* Extra types */
#define NEED_SYS_PARAM_H 1 /* Some systems need this */
#define NEED_LIMITS_H 0 /* Kernel limits */
#define NEED_STDARG_H 1 /* va_list, etc */
#define NEED_ERRNO_H 1 /* errno */
#define NEED_CTYPE_H 0 /* isdigit(), etc */
#define NEED_NETINET_IN_H 1 /* in_addr, sockaddr_in, etc */
#define NEED_ARPA_INET_H 1 /* inet_ntoa(), inet_aton(), etc */
#define NEED_STDIO_H 1 /* Standard C UNIX functions */
#define NEED_STDLIB_H 1 /* malloc(), exit(), atoi(), etc */
#define NEED_TIME_H 1 /* time(), etc */
#define NEED_SYSCTL_H 0 /* sysctl(), etc */
#define NEED_SYS_STAT_H 0 /* chmod(), mkdir(), etc */
#define NEED_SYS_UIO_H 0 /* iovec, etc */
#define NEED_FCNTL_H 1 /* open(), creat(), fcntl(), etc */
#define NEED_SYS_IOCTL_H 0 /* ioctl(), etc */
#define NEED_SYS_FILIO_H 0 /* Solaris need this for ioctl(), etc */
#define NEED_UNISTD_H 1 /* Unix standard functions */
#define NEED_STRING_H 1 /* C string functions */
#define NEED_SIGNAL_H 0 /* Signal functions */
#define NEED_SYS_SOCKET_H 1 /* Socket functions */
#define NEED_NETDB_H 1 /* Network database functions */
#define NEED_ARPA_NAMESER_H 0 /* Nameserver definitions */
#define NEED_GETUSERPW_HEADERS 0 /* Functions to retrive system passwords */
#define NEED_ARES 1 /* Functions needed for ares */
#define NEED_SSL 1 /* Needed for SSL support */
#include "includes.h"
#include "irc.h"
#include "conf.h"
#include "access_conf.h"
#include "conn_conf.h"
#include "listen.h"
#include "client.h"
#include "client_connection.h"
#include "client_handle.h"
#include "client_auth.h"
#include "client_notice.h"
#include "conn.h"
#include "conn_sendq.h"
#include "chan.h"
#include "chan_user.h"
/* VARIABLES - JONAS (31.07.2001) */
#if ARES
extern ares_channel Ares_Channel;
#endif
extern struct Conf_Struct ConfS;
extern struct Client_Struct *Client_Head;
extern struct Client_Struct *Client_Tail;
extern unsigned long int G_Clients;
extern struct AccessConf_Struct *Allow_Head;
extern struct AccessConf_Struct *Deny_Head;
extern unsigned short int Access_CheckFirst;
extern unsigned short int Access_Default;
extern struct Conn_Struct *Conn_Head;
/* CLIENT_ACCESSCHECK FUNCTION - JONAS (31.07.2001) */
unsigned short int client_accesscheck(struct Client_Struct *ClientS) {
struct AccessConf_Struct *AllowConf = NULL;
struct AccessConf_Struct *DenyConf = NULL;
for (AllowConf = Allow_Head ; AllowConf != NULL ; AllowConf = AllowConf->Next) {
if ((ClientS->HostName != NULL) && (strwm(AllowConf->Host, ClientS->HostName) == TRUE)) { break; }
if (strwm(AllowConf->Host, ClientS->HostIPS) == TRUE) { break; }
}
for (DenyConf = Deny_Head ; DenyConf != NULL ; DenyConf = DenyConf->Next) {
if ((ClientS->HostName != NULL) && (strwm(DenyConf->Host, ClientS->HostName) == TRUE)) { break; }
if (strwm(DenyConf->Host, ClientS->HostIPS) == TRUE) { break; }
}
if (Access_CheckFirst == ACCESS_ALLOW) {
if (AllowConf != NULL) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Allowing connection from %s(%s): Matching ALLOW rule: \"%s\".", ClientS->HostName, ClientS->HostIPS, AllowConf->Host);
return(TRUE);
}
if (DenyConf != NULL) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Refusing connection from %s(%s): Matching DENY rule: \"%s\".", ClientS->HostName, ClientS->HostIPS, DenyConf->Host);
if (ConfS.ClientShowDenyReason == TRUE) { client_close(ClientS, "Connection denied: Your host is matching DENY rule \"%s\": %s", DenyConf->Host, DenyConf->Reason); }
else { client_close(ClientS, "Connection denied from your host: %s", DenyConf->Reason); }
return(FALSE);
}
}
else {
if (DenyConf != NULL) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Refusing connection from %s(%s): Matching DENY rule: \"%s\".", ClientS->HostName, ClientS->HostIPS, DenyConf->Host);
if (ConfS.ClientShowDenyReason == TRUE) { client_close(ClientS, "Connection denied: Your host is matching DENY rule \"%s\": %s", DenyConf->Host, DenyConf->Reason); }
else { client_close(ClientS, "Connection denied from your host: %s", DenyConf->Reason); }
return(FALSE);
}
if (AllowConf != NULL) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Allowing connection from %s(%s): Matching ALLOW rule: \"%s\".", ClientS->HostName, ClientS->HostIPS, AllowConf->Host);
return(TRUE);
}
}
if (Access_Default == ACCESS_ALLOW) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Allowing connection from %s(%s): No matching rule, default = ALLOW.", ClientS->HostName, ClientS->HostIPS);
return(TRUE);
}
else {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Refusing connection from %s(%s): No matching rule, default = DENY.", ClientS->HostName, ClientS->HostIPS);
if (ConfS.ClientShowDenyReason == TRUE) { client_close(ClientS, "Connection denied: No matching rule for your host, defaulting to DENY."); }
else { client_close(ClientS, "Connection denied from your host: %s", ACCESS_CONF_NOREASON); }
return(FALSE);
}
}
/* CLIENT_WELCOME FUNCTION - JONAS (01.07.2000) */
void client_welcome(struct Client_Struct *ClientS) {
struct Conn_Struct *P_ConnS = NULL;
struct Client_Struct *P_ClientS = NULL;
char Line1[LINELEN+1] = "";
char Line2[LINELEN+1] = "";
char *TempPT = NULL;
char *ConnsPT = NULL;
char *ClientsPT = NULL;
unsigned short int ConnsCount = 0;
unsigned short int UsersCount = 0;
unsigned short int Len = 0;
client_notice(ClientS, "Authenticated as %s!", ClientS->User);
client_notice(ClientS, "Commands are: PCONNLIST, PATTACH, PDETACH, PCONNECT, PQUIT, READLOG and ERASELOG.");
for (P_ConnS = Conn_Head , memset(&Line1, 0, LINELEN+1) , memset(&Line2, 0, LINELEN+1) ; P_ConnS != NULL ; P_ConnS = P_ConnS->Next) {
if (strcmp(P_ConnS->User, ClientS->User) != FALSE) { continue; }
++ConnsCount;
if (Conn_IsWelcome(P_ConnS)) { snprintf(Line1, LINELEN+1, "%s(Connected %s:%ld)", P_ConnS->Name, P_ConnS->ServerHostName, P_ConnS->ServerPortH); }
else { snprintf(Line1, LINELEN+1, "%s(Disconnected)", P_ConnS->Name); }
snprintf(Line2, LINELEN+1, ", %s", Line1);
if (ConnsPT != NULL) {
Len = strlen(ConnsPT) + strlen(Line2);
if (Len > IRCNOTICELEN) {
client_notice(ClientS, "%s.", ConnsPT);
FREE(ConnsPT);
}
}
if (ConnsPT == NULL) {
TempPT = straddbuffer(ConnsPT, "Your IRC connections: ", IRCNOTICELEN, 0);
if (TempPT == NULL) { break; }
ConnsPT = TempPT;
TempPT = straddbuffer(ConnsPT, Line1, IRCNOTICELEN, 0);
if (TempPT == NULL) { FREE(ConnsPT); break; }
ConnsPT = TempPT;
continue;
}
TempPT = straddbuffer(ConnsPT, Line2, IRCNOTICELEN, 0);
if (TempPT == NULL) { FREE(ConnsPT); break; }
ConnsPT = TempPT;
}
if (ConnsPT != NULL) { client_notice(ClientS, "%s.", ConnsPT); FREE(ConnsPT); }
if (ConnsCount <= 0) { client_notice(ClientS, "You have no connections."); }
for (P_ClientS = Client_Head , memset(&Line1, 0, LINELEN+1) , memset(&Line2, 0, LINELEN+1) ; P_ClientS != NULL ; P_ClientS = P_ClientS->Next) {
if (!Client_IsVerified(P_ClientS)) { continue; }
if (strcmp(P_ClientS->User, ClientS->User) != FALSE) { continue; }
++UsersCount;
if (P_ClientS->ConnS == NULL) { snprintf(Line1, LINELEN+1, "%s(%s) Detached", P_ClientS->HostName, P_ClientS->HostIPS); }
else { snprintf(Line1, LINELEN+1, "%s(%s) Attached=%s", P_ClientS->HostName, P_ClientS->HostIPS, P_ClientS->ConnS->Name); }
snprintf(Line2, LINELEN+1, ", %s", Line1);
if (ClientsPT != NULL) {
Len = strlen(ClientsPT) + strlen(Line2);
if (Len > IRCNOTICELEN) {
client_notice(ClientS, "%s.", ClientsPT);
FREE(ClientsPT);
}
}
if (ClientsPT == NULL) {
TempPT = straddbuffer(ClientsPT, "Clients from your host: ", IRCNOTICELEN, 0);
if (TempPT == NULL) { break; }
ClientsPT = TempPT;
TempPT = straddbuffer(ClientsPT, Line1, IRCNOTICELEN, 0);
if (TempPT == NULL) { break; }
ClientsPT = TempPT;
continue;
}
TempPT = straddbuffer(ClientsPT, Line2, IRCNOTICELEN, 0);
if (TempPT == NULL) { FREE(ClientsPT); break; }
ClientsPT = TempPT;
}
if (ClientsPT != NULL) {
client_notice(ClientS, "%s.", ClientsPT);
FREE(ClientsPT);
}
client_addsend(ClientS, ":%s 422 * :No MOTD here.", HOSTNAME);
}
/* CLIENT_ATTACH FUNCTION - JONAS (01.07.2000) */
void client_attach(struct Client_Struct *ClientS, struct Conn_Struct *ConnS) {
unsigned short int Count = 0;
unsigned short int Users = 0;
unsigned short int Index = 0;
struct Chan_Struct *ChanS = NULL;
struct ChanUser_Struct *ChanUser = NULL;
char *NamesPT = NULL;
unsigned short int NameIndex = 0;
unsigned short int NameLen = 0;
unsigned short int DumbLen = 0;
unsigned short int UserLen = 0;
unsigned short int NewLen = 0;
char *DumbPT = NULL;
if (ConnS->NumClients >= ConnS->MaxClients) { client_notice(ClientS, "Connection %s has %d attached clients, only %d clients are allowed.", ConnS->Name, ConnS->NumClients, ConnS->MaxClients); return; }
if (!Conn_IsWelcome(ConnS)) {
ClientS->ConnS = ConnS;
++ConnS->NumClients;
client_notice(ClientS, "Attached on connection %s: Not connected.", ConnS->Name);
client_noticeallbutone(ClientS, "Client %s (%s) attached on connection %s.", ClientS->HostName, ClientS->HostIPS, ConnS->Name);
return;
}
if (ConnS->ServerName == NULL) { client_notice(ClientS, "Internal error."); return; }
for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
if (!Chan_IsNames(ChanS)) { client_notice(ClientS, "Please wait for %s/NAMES to complete.", ChanS->Chan); return; }
}
if (ConnS->NumClients <= 0) {
if ((ConnConf_IsAutoAway(ConnS)) && (Conn_IsAway(ConnS))) { conn_addsendq(ConnS, "AWAY"); }
if (ConnConf_IsPublicAway(ConnS)) {
for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) { conn_addsendq(ConnS, "PRIVMSG %s :\1ACTION %s\1", ChanS->Chan, ConnS->PublicAttachMsg); }
}
if ((!Conn_IsSentNick(ConnS)) && (!Conn_IsSentAwayNick(ConnS)) && (Conn_IsAwayNick(ConnS)) && (strcasecmp(ConnS->IRCNick, ConnS->Nick) != FALSE) && (strcasecmp(ConnS->Nick, ConnS->AwayNick) != FALSE)) {
Conn_SetSentNick(ConnS);
ConnS->NicksIndex = 0;
conn_addsendq(ConnS, "NICK %s", ConnS->Nick);
}
}
for (Count = 0 ; Count <= 4 ; ++Count) {
if (ConnS->Welcome[Count] != NULL) { client_addsend(ClientS, ":%s 00%d %s %s", ConnS->ServerName, Count, ConnS->IRCNick, ConnS->Welcome[Count]); }
}
for (Index = 0 ; Index < ConnS->ISupportLines ; ++Index) {
if (ConnS->ISupport_String[Index] != NULL) { client_addsend(ClientS, ":%s 00%d %s %s", ConnS->ServerName, IRC_GENERIC_RPL_ISUPPORT, ConnS->IRCNick, ConnS->ISupport_String[Index]); }
}
for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
assert(ChanS->Me != NULL);
client_addsend(ClientS, ":%s JOIN :%s", ChanS->Me->NUH, ChanS->Chan);
FREE(NamesPT);
NameLen = 12;
NameLen += strlen(ConnS->ServerName);
NameLen += strlen(ChanS->Me->Nick);
NameLen += strlen(ChanS->Chan);
DumbPT = realloc(NamesPT, NameLen);
if (DumbPT == NULL) {
client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
free(NamesPT);
return;
}
NamesPT = DumbPT;
sprintf(NamesPT, ":%s 353 %s = %s :", ConnS->ServerName, ChanS->Me->Nick, ChanS->Chan);
NameIndex = NameLen - 1;
Count = 0;
Users = 0;
for (ChanUser = ChanS->User_Head ; ChanUser != NULL ; ChanUser = ChanUser->Next) {
Count++;
UserLen = strlen(ChanUser->Nick);
if (ChanUser_IsOP(ChanUser)) { UserLen++; }
else if (ChanUser_IsVoice(ChanUser)) { UserLen++; }
if (Count > 1) { UserLen++; }
DumbLen = NameLen + UserLen - 1;
if (DumbLen > IRCMSGLEN) {
client_addsend(ClientS, "%s", NamesPT);
memset(NamesPT, 0, NameLen);
FREE(NamesPT);
NameLen = 12;
NameLen += strlen(ConnS->ServerName);
NameLen += strlen(ChanS->Me->Nick);
NameLen += strlen(ChanS->Chan);
DumbPT = realloc(NamesPT, NameLen);
if (DumbPT == NULL) {
client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
free(NamesPT);
return;
}
NamesPT = DumbPT;
sprintf(NamesPT, ":%s 353 %s = %s :", ConnS->ServerName, ChanS->Me->Nick, ChanS->Chan);
Count = 1;
NameIndex = NameLen - 1;
UserLen = strlen(ChanUser->Nick);
if (ChanUser_IsOP(ChanUser)) { UserLen++; }
else if (ChanUser_IsVoice(ChanUser)) { UserLen++; }
DumbLen = NameLen + UserLen - 1;
assert(DumbLen <= IRCMSGLEN);
}
NewLen = NameLen + UserLen;
DumbPT = realloc(NamesPT, NewLen);
if (DumbPT == NULL) {
client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
free(NamesPT);
return;
}
NamesPT = DumbPT;
DumbPT += NameIndex;
if (Count > 1) {
*DumbPT = ' ';
DumbPT++;
}
if (ChanUser_IsOP(ChanUser)) {
*DumbPT = '@';
DumbPT++;
}
else if (ChanUser_IsVoice(ChanUser)) {
*DumbPT = '+';
DumbPT++;
}
Users++;
strcpy(DumbPT, ChanUser->Nick);
NameIndex += UserLen;
NameLen += UserLen;
}
if (Count > 0) {
client_addsend(ClientS, "%s", NamesPT);
memset(NamesPT, 0, NameLen);
FREE(NamesPT);
NameLen = 0;
}
assert(ChanS->Users == Users);
client_addsend(ClientS, ":%s 366 %s %s :End of NAMES from proxy", ConnS->ServerName, ChanS->Me->Nick, ChanS->Chan);
}
Conn_SetAttached(ConnS);
++ConnS->NumClients;
ClientS->ConnS = ConnS;
client_notice(ClientS, "Attached on connection %s %s(%s):%ld \"%s\"", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName);
client_noticeallbutone(ClientS, "Client %s (%s) attached on connection %s.", ClientS->HostName, ClientS->HostIPS, ConnS->Name);
}
/* CLIENT_DETACH FUNCTION - JONAS (01.07.2000) */
void client_detach(struct Client_Struct *ClientS) {
assert(ClientS != NULL);
assert(ClientS->ConnS != NULL);
--ClientS->ConnS->NumClients;
if (Conn_IsWelcome(ClientS->ConnS)) {
if ((Client_IsSocket(ClientS)) && (Client_IsVerified(ClientS))) {
client_close(ClientS, "Detached from connection %s %s(%s):%ld \"%s\"", ClientS->ConnS->Name, ClientS->ConnS->ServerHostName, ClientS->ConnS->ServerHostIPS, ClientS->ConnS->ServerPortH, ClientS->ConnS->ServerName);
}
if (ClientS->ConnS->NumClients <= 0) {
if ((ConnConf_IsAutoAway(ClientS->ConnS)) && (!Conn_IsAway(ClientS->ConnS))) { conn_addsendq(ClientS->ConnS, "AWAY :%s", ClientS->ConnS->AwayMsg); }
if (ConnConf_IsPublicAway(ClientS->ConnS)) {
struct Chan_Struct *ChanS = NULL;
for (ChanS = ClientS->ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) { conn_addsendq(ClientS->ConnS, "PRIVMSG %s :\1ACTION %s\1", ChanS->Chan, ClientS->ConnS->PublicDetachMsg); }
}
if ((!Conn_IsSentNick(ClientS->ConnS)) && (!Conn_IsSentAwayNick(ClientS->ConnS)) && (!Conn_IsAwayNick(ClientS->ConnS)) && (strcasecmp(ClientS->ConnS->IRCNick, ClientS->ConnS->AwayNick) != FALSE) && (strcasecmp(ClientS->ConnS->Nick, ClientS->ConnS->AwayNick) != FALSE)) {
Conn_SetSentAwayNick(ClientS->ConnS);
ClientS->ConnS->NicksIndex = 0;
conn_addsendq(ClientS->ConnS, "NICK %s", ClientS->ConnS->AwayNick);
}
}
}
else {
if ((Client_IsSocket(ClientS)) && (Client_IsVerified(ClientS))) {
client_notice(ClientS, "Detached from connection %s: Not connected.", ClientS->ConnS->Name);
}
}
client_noticeallbutone(ClientS, "Client %s (%s) detached from connection %s.", ClientS->HostName, ClientS->HostIPS, ClientS->ConnS->Name);
ClientS->ConnS = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1