/* * ---------------------------------------------------------------- * Night Light IRC Proxy - Client Handle Functions * ---------------------------------------------------------------- * Copyright (C) 1997-2007 Jonas Kvinge * 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; }