/* * ---------------------------------------------------------------- * Night Light IRC Proxy - Client Connection 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 (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); }