/*
* ----------------------------------------------------------------
* Night Light IRC Proxy - Client Connection 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 (24.11.2007)
*
*/
#define CLIENT_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 "listen.h"
#include "client.h"
#include "client_connection.h"
#include "client_handle.h"
#include "client_notice.h"
#include "client_auth.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 /* ARES */
extern struct Conf_Struct ConfS;
extern struct Client_Struct *Client_Head;
extern struct Client_Struct *Client_Tail;
extern unsigned long int G_Clients;
/* CLIENT_RESOLVE FUNCTION - JONAS (01.07.2000) */
void client_resolve(struct Client_Struct *ClientS) {
#if !ARES
struct hostent *HostEnt = NULL;
#endif /* !ARES */
assert(ClientS != NULL);
if (!Host_IsHostIPToName(ClientS->ResolveFlags)) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Attempting to resolve client IP-address %s to hostname...", ClientS->HostIPS);
Host_SetResolving(ClientS->ResolveFlags);
#if ARES
#if IPV6_SUPPORT
if (Client_IsIPv6(ClientS)) {
ares_gethostbyaddr(Ares_Channel, &ClientS->INAddr6, sizeof(ClientS->INAddr6), AF_INET6, (ares_host_callback) client_hostiptoname, ClientS);
}
else {
#endif /* IPV6_SUPPORT */
ares_gethostbyaddr(Ares_Channel, &ClientS->INAddr, sizeof(ClientS->INAddr), AF_INET, (ares_host_callback) client_hostiptoname, ClientS);
#if IPV6_SUPPORT
}
#endif /* IPV6_SUPPORT */
return;
#else /* ARES */
#if IPV6_SUPPORT
if (Client_IsIPv6(ClientS)) { HostEnt = gethostbyaddr(&ClientS->INAddr6, sizeof(ClientS->INAddr6), AF_INET6); }
else {
#endif
HostEnt = gethostbyaddr(&ClientS->INAddr, sizeof(ClientS->INAddr), AF_INET);
#if IPV6_SUPPORT
}
#endif
client_hostiptoname(ClientS, errno, HostEnt);
#endif /* ARES */
}
if (!Host_IsHostNameToIP(ClientS->ResolveFlags)) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Attempting to resolve client hostname %s (PTR on IP-Address %s) to IP-address...", ClientS->HostName, ClientS->HostIPS);
Host_SetResolving(ClientS->ResolveFlags);
assert(ClientS->HostName != NULL);
#if ARES
#if IPV6_SUPPORT
if (Client_IsIPv6(ClientS)) {
ares_gethostbyname(Ares_Channel, ClientS->HostName, AF_INET6, (ares_host_callback) client_hostnametoip, ClientS);
}
else {
#endif /* IPV6_SUPPORT */
ares_gethostbyname(Ares_Channel, ClientS->HostName, AF_INET, (ares_host_callback) client_hostnametoip, ClientS);
#if IPV6_SUPPORT
}
#endif /* IPV6_SUPPORT */
return;
#else /* ARES */
HostEnt = gethostbyname(ClientS->HostName);
client_hostnametoip(ClientS, errno, HostEnt);
#endif /* ARES */
}
Host_SetResolved(ClientS->ResolveFlags);
sysprint(BITMASK_MAIN, "New client from %s resolved to %s.", ClientS->HostIPS, ClientS->HostName);
ClientS->Time = NOW;
if (G_Clients > ConfS.MaxClients) { client_close(ClientS, "The maximum number of clients has been reached."); return; }
if (client_accesscheck(ClientS) == FALSE) { return; }
client_notice(ClientS, "%s v%s", SHORTNAME, VERSION);
client_notice(ClientS, "%s", COPYRIGHT);
}
/* CLIENT_HOSTIPTONAME FUNCTION - JONAS (01.03.2000) */
#if HAVE_CARES_CALLBACK_TIMEOUTS
void client_hostiptoname(void *ArgPT, int ErrNo, int Timeouts, struct hostent *HostEnt) {
#else
void client_hostiptoname(void *ArgPT, int ErrNo, struct hostent *HostEnt) {
#endif
struct Client_Struct *ClientS = ArgPT;
assert(ClientS != NULL);
Host_ClearResolving(ClientS->ResolveFlags);
if (!Client_IsSocket(ClientS)) { return; } /* The user has already closed the connection */
Host_SetHostIPToName(ClientS->ResolveFlags);
if ((HostEnt == NULL) || (HostEnt->h_name == NULL) || (strcmp(HostEnt->h_name, "") == FALSE)) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Unable to resolve client IP-address %s to hostname: [%d] %s", ClientS->HostIPS, ErrNo, res_strerror(ErrNo));
ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
Host_SetHostNameToIP(ClientS->ResolveFlags);
}
else {
ClientS->HostName = strrealloc(ClientS->HostName, HostEnt->h_name);
if (aerrno != AESUCCESS) {
client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
return;
}
Host_SetHostIPToName(ClientS->ResolveFlags);
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client IP-address %s to hostname %s.", ClientS->HostIPS, ClientS->HostName);
}
#if ARES
client_resolve(ClientS);
#endif /* ARES */
}
/* CLIENT_HOSTNAMETOIP FUNCTION - JONAS (01.03.2000) */
#if HAVE_CARES_CALLBACK_TIMEOUTS
void client_hostnametoip(void *ArgPT, int ErrNo, int Timeouts, struct hostent *HostEnt) {
#else
void client_hostnametoip(void *ArgPT, int ErrNo, struct hostent *HostEnt) {
#endif
struct Client_Struct *ClientS = ArgPT;
const char *HostIPPT = NULL;
struct in_addr INAddr;
#if IPV6_SUPPORT
struct in6_addr INAddr6;
char Host[INET6_ADDRSTRLEN+1] = "";
#endif
assert(ClientS != NULL);
Host_ClearResolving(ClientS->ResolveFlags);
if (!Client_IsSocket(ClientS)) { return; } /* The user has already closed the connection */
Host_SetHostNameToIP(ClientS->ResolveFlags);
if ((HostEnt == NULL) || (HostEnt->h_length < 1)) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Unable to resolve client hostname %s (PTR on IP-Address %s) to IP-address: [%d] %s", ClientS->HostName, ClientS->HostIPS, ErrNo, res_strerror(ErrNo));
ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
}
else {
#if IPV6_SUPPORT
if (Client_IsIPv6(ClientS)) {
memset(&INAddr6, 0, sizeof(INAddr6));
memcpy(&INAddr6, HostEnt->h_addr, HostEnt->h_length);
HostIPPT = inet_ntop(AF_INET6, &INAddr6, Host, INET6_ADDRSTRLEN);
if (HostIPPT == NULL) {
client_close(ClientS, "inet_ntop() failed: [%d] %s", errno, strerror(errno));
return;
}
}
else {
memset(&INAddr, 0, sizeof(INAddr));
memcpy(&INAddr, HostEnt->h_addr, HostEnt->h_length);
HostIPPT = inet_ntop(AF_INET, &INAddr, Host, INET6_ADDRSTRLEN);
if (HostIPPT == NULL) {
client_close(ClientS, "inet_ntop() failed: [%d] %s", errno, strerror(errno));
return;
}
}
#else
memset(&INAddr, 0, sizeof(INAddr));
memcpy(&INAddr, HostEnt->h_addr, HostEnt->h_length);
HostIPPT = inet_ntoa(INAddr);
if (HostIPPT == NULL) {
client_close(ClientS, "inet_ntoa() failed: [%d] %s", errno, strerror(errno));
return;
}
#endif
if (strcmp(ClientS->HostIPS, HostIPPT) == FALSE) {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client hostname %s (PTR on IP-Address %s) to correct IP-address %s.", ClientS->HostName, ClientS->HostIPS, ClientS->HostIPS);
}
else {
DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client hostname %s (PTR on IP-Address %s) to a different IP-address %s.", ClientS->HostName, ClientS->HostIPS, HostIPPT);
ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
}
}
#if ARES
client_resolve(ClientS);
#endif /* ARES */
}
/* CLIENT_ADDSEND FUNCTION - JONAS (01.07.2000) */
void client_addsend(struct Client_Struct *ClientS, const char *const LinePT, ...) {
char Line[IRCMSGLEN+1] = "";
char LineCRLF[IRCMSGCRLFLEN+1] = "";
va_list Args = { 0 };
unsigned long int Len = 0;
unsigned long int OldLen = 0;
unsigned long int NewLen = 0;
char *SendBufferPT = NULL;
assert(ClientS != NULL);
assert(LinePT != NULL);
va_start(Args, LinePT);
vsnprintf(Line, IRCMSGLEN+1, LinePT, Args);
va_end(Args);
snprintf(LineCRLF, IRCMSGCRLFLEN+1, "%s\r\n", Line);
Len = strlen(LineCRLF);
if (ClientS->SendBuffer == NULL) { OldLen = 0; }
else { OldLen = strlen(ClientS->SendBuffer); }
NewLen = OldLen + Len + 1;
SendBufferPT = realloc(ClientS->SendBuffer, NewLen);
if (SendBufferPT == NULL) { return; }
ClientS->SendBuffer = SendBufferPT;
SendBufferPT = SendBufferPT + OldLen;
strcpy(SendBufferPT, LineCRLF);
}
/* CLIENT_CLOSE FUNCTION - JONAS (09.06.2001) */
void client_close(struct Client_Struct *ClientS, const char *const LinePT, ...) {
char Line[LINELEN+1] = "";
va_list Args = { 0 };
assert(ClientS != NULL);
assert(LinePT != NULL);
if (Client_IsSentError(ClientS)) { return; }
va_start(Args, LinePT);
vsnprintf(Line, LINELEN+1, LinePT, Args);
va_end(Args);
sysprint(BITMASK_MAIN, "Closing connection to client %s (%s): %s", ClientS->HostName, ClientS->HostIPS, Line);
if (Client_IsVerified(ClientS)) { client_noticeallbutone(ClientS, "Closing connection to client %s (%s): %s", ClientS->HostName, ClientS->HostIPS, Line); }
Client_SetSentError(ClientS);
ClientS->SentErrorTime = time(NULL);
client_addsend(ClientS, "ERROR :%s", Line);
}
/* CLIENT_CLEANUP FUNCTION - JONAS (01.07.2000) */
void client_cleanup(struct Client_Struct *ClientS, const char *const LinePT, ...) {
char Line[LINELEN+1] = "";
va_list Args = { 0 };
assert(ClientS != NULL);
assert(LinePT != NULL);
va_start(Args, LinePT);
vsnprintf(Line, LINELEN+1, LinePT, Args);
va_end(Args);
sysprint(BITMASK_MAIN, "%s", Line);
if (Client_IsVerified(ClientS)) { client_noticeallbutone(ClientS, "%s", Line); }
if (Client_IsSocket(ClientS)) {
close(ClientS->FD);
ClientS->FD = FD_NONE;
}
#if SSL_SUPPORT
if (Client_IsSSL(ClientS)) {
if (ClientS->Cert != NULL) {
X509_free(ClientS->Cert);
ClientS->Cert = NULL;
}
if (ClientS->SSL_H != NULL) {
SSL_shutdown(ClientS->SSL_H);
SSL_free(ClientS->SSL_H);
ClientS->SSL_H = NULL;
}
}
#endif /* SSL_SUPPORT */
ClientS->Flags = 0;
FREE(ClientS->RecvBuffer);
FREE(ClientS->SendBuffer);
if (ClientS->Who_Head != NULL) {
struct Who_Struct *WhoS = NULL;
for (WhoS = ClientS->Who_Head; WhoS != NULL; WhoS = WhoS->NextForClient) {
WhoS->ClientS = NULL;
}
ClientS->Who_Head = NULL;
}
if (ClientS->ConnS != NULL) { client_detach(ClientS); }
}
/* CLIENT_CLOSEALL FUNCTION - JONAS (09.06.2001) */
unsigned short int client_closeall(const char *const MessagePT, ...) {
char Message[LINELEN+1] = "";
va_list Args = { 0 };
unsigned short int Count = 0;
struct Client_Struct *ClientS = NULL;
assert(MessagePT != NULL);
va_start(Args, MessagePT);
vsnprintf(Message, LINELEN+1, MessagePT, Args);
va_end(Args);
for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
if (!Client_IsSentError(ClientS)) { client_close(ClientS, "%s", Message); }
++Count;
}
return(Count);
}
syntax highlighted by Code2HTML, v. 0.9.1