/*
* ----------------------------------------------------------------
* Night Light IRC Proxy - Connection I/O 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 CONN_IO_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 1 /* 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 1 /* ioctl(), etc */
#define NEED_SYS_FILIO_H 1 /* 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 0 /* Functions needed for ares */
#define NEED_SSL 1 /* Needed for SSL support */
#include "includes.h"
#include "irc.h"
#include "conf.h"
#include "conn_conf.h"
#include "conn.h"
#include "conn_io.h"
#if SSL_SUPPORT
#include "conn_io_ssl.h"
#endif /* SSL_SUPPORT */
#include "conn_connection.h"
#include "conn_sendq.h"
#include "conn_log.h"
#include "conn_parser.h"
#include "conn_ignore.h"
#include "chan.h"
#include "chan_mode.h"
#include "client_notice.h"
/* VARIABLES - JONAS (31.07.2001) */
extern struct Conf_Struct ConfS;
extern struct Conn_Struct *Conn_Head;
/* CONN_FDS FUNCTION - JONAS (22.07.2001) */
void conn_fds(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {
struct Conn_Struct *ConnS = NULL;
struct Conn_Struct *ConnS_DEL = NULL;
struct Chan_Struct *ChanS = NULL;
time_t Duration = 0;
for (ConnS = Conn_Head ; ConnS != NULL ;) {
if (NOW > ConnS->IgnoreExpireTime) { conn_ignoreexpire(ConnS); }
FAKELOOP {
if (!Conn_IsConnect(ConnS)) { break; }
if (ConnS->NumSendQs > 0) { conn_flushsendq(ConnS); }
if (!Conn_IsWelcome(ConnS)) {
Duration = (NOW - ConnS->ConnectTime);
if (Duration >= CONN_CONNECTTIMEOUT) { conn_disconnect(ConnS, "Connection %s: Connection attempt to server %s(%s):%ld timed out.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); break; }
break;
}
if (Conn_IsSentQuit(ConnS)) {
Duration = (NOW - ConnS->SentQuitTime);
if (Duration >= CONN_QUITTIMEOUT) { conn_disconnect(ConnS, "Connection %s: Timeout while waiting for server %s(%s):%ld to close connection.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); break; }
break;
}
if (!Conn_IsWelcome(ConnS)) { break; } /* Don't PING the connection if we haven't received 001 --Jonas */
/* LAG CHECK */
if (Conn_IsSentPing(ConnS)) {
Duration = (NOW - ConnS->SentPingTime);
if (Duration >= CONN_PINGTIMEOUT) { conn_quit(ConnS, "No response from server in %s", strduration(Duration)); }
}
else {
Duration = (NOW - ConnS->LastRecvTime);
if (Duration >= CONN_IDLETIMEBEFOREPING) {
Conn_SetSentPing(ConnS);
ConnS->SentPingTime = NOW;
conn_addsendq(ConnS, "PING :PROXY");
}
}
/* REGAIN NICK */
if ((ConnConf_IsRegainNick(ConnS)) && (!Conn_IsSentISON(ConnS)) && (!Conn_IsSentNick(ConnS)) && (!Conn_IsSentAwayNick(ConnS)) && (strcasecmp(((Conn_IsAwayNick(ConnS)) ? ConnS->AwayNick : ConnS->Nick), ConnS->IRCNick) != FALSE) && (ConnS->NumClients <= 0)) {
Duration = (NOW - ConnS->SentISONTime);
if (Duration >= CONN_ISONTIME) {
Conn_SetSentISON(ConnS);
ConnS->SentISONTime = NOW;
conn_addsendq(ConnS, "ISON :%s", ((Conn_IsAwayNick(ConnS)) ? ConnS->AwayNick : ConnS->Nick));
}
}
for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
if (ChanS->SentModes > 0) {
Duration = (NOW - ChanS->CheckSentModesTime);
if (Duration >= CHANMODEEXPIRESENTMODESTIME) { chan_expiresentmodes(ConnS, ChanS); }
}
if (ChanS->Modes > 0) {
Duration = (NOW - ChanS->FlushModesTime);
if (Duration >= CHANMODEFLUSHMODESTIME) { chan_flushmodes(ConnS, ChanS); }
}
}
}
if ((Conn_IsSocket(ConnS)) && (Host_IsResolved(ConnS->ResolveFlags))) {
FD_SET(ConnS->FD, ReadFDS);
if (ConnS->SendBuffer != NULL) { FD_SET(ConnS->FD, WriteFDS); }
}
if ((Conn_IsRemove(ConnS)) && (!Conn_IsConnectProc(ConnS)) && (!Conn_IsSocket(ConnS)) && (!Host_IsResolving(ConnS->ResolveFlags)) && (!Host_IsResolving(ConnS->ServerResolveFlags))) {
ConnS_DEL = ConnS;
ConnS = ConnS->Next;
conn_rem(ConnS_DEL);
continue;
}
if ((ConnConf_IsAutoConnect(ConnS)) && (!Conn_IsRemove(ConnS)) && (!Conn_IsConnectProc(ConnS)) && (!Conn_IsSocket(ConnS))) {
Duration = (NOW - ConnS->ConnectProcTime);
if (Duration >= CONN_INTERVAL) {
#if SSL_SUPPORT
if (ConfS.SSLSupport == TRUE) { conn_connect(ConnS); }
else if (!ConnConf_IsSSL(ConnS)) { conn_connect(ConnS); }
#else
if (!ConnConf_IsSSL(ConnS)) { conn_connect(ConnS); }
#endif
}
}
ConnS = ConnS->Next;
continue;
}
}
/* CONN_IO FUNCTION - JONAS (01.07.2000) */
void conn_io(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {
struct Conn_Struct *ConnS = NULL;
assert(ReadFDS != NULL);
assert(WriteFDS != NULL);
for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
assert(*FDS >= 0);
if (*FDS <= 0) { return; }
if (Conn_IsSocket(ConnS)) {
if (FD_ISSET(ConnS->FD, ReadFDS)) { *FDS = *FDS - 1; }
if (FD_ISSET(ConnS->FD, WriteFDS)) { *FDS = *FDS - 1; }
}
if (Conn_IsSocket(ConnS)) {
if (FD_ISSET(ConnS->FD, ReadFDS)) {
#if SSL_SUPPORT
if (ConnConf_IsSSL(ConnS)) { conn_recv_ssl(ConnS); }
else {
#endif
conn_recv(ConnS);
#if SSL_SUPPORT
}
#endif
}
}
if (Conn_IsSocket(ConnS)) {
if (FD_ISSET(ConnS->FD, WriteFDS)) {
#if SSL_SUPPORT
if (ConnConf_IsSSL(ConnS)) { conn_send_ssl(ConnS); }
else {
#endif
conn_send(ConnS);
#if SSL_SUPPORT
}
#endif
}
}
}
}
/* CONN_RECV FUNCTION - JONAS (01.07.2000) */
void conn_recv(struct Conn_Struct *ConnS) {
signed long int Result = 0;
unsigned long int OldLen = 0;
unsigned long int NewLen = 0;
char RecvBuffer[RECVBUFFERLEN+1] = "";
char *RecvBufferPT = NULL;
assert(ConnS != NULL);
assert(Conn_IsSocket(ConnS));
assert(ConnS->FD != FD_NONE);
if ((Conn_IsConnectProc(ConnS)) && (Host_IsResolved(ConnS->ResolveFlags))) {
conn_connect(ConnS);
if (!Conn_IsSocket(ConnS)) { return; }
}
ConnS->LastRecvTime = NOW;
do {
memset(&RecvBuffer, 0, sizeof(RecvBuffer));
Result = recv(ConnS->FD, RecvBuffer, RECVBUFFERLEN, MSG_NOSIGNAL);
if (Result <= ERROR) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { break; }
conn_disconnect(ConnS, "Connection %s: Read error to %s(%s):%ld: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, errno, strerror(errno));
return;
}
if (Result == 0) {
if (ConnS->RecvBuffer != NULL) { conn_parser(ConnS); }
conn_disconnect(ConnS, "Connection %s: EOF to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH);
return;
}
if (ConnS->RecvBuffer == NULL) { OldLen = 0; }
else { OldLen = strlen(ConnS->RecvBuffer); }
NewLen = OldLen + Result + 1;
RecvBufferPT = realloc(ConnS->RecvBuffer, NewLen);
if (RecvBufferPT == NULL) {
conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
return;
}
ConnS->RecvBuffer = RecvBufferPT;
RecvBufferPT += OldLen;
strcpy(RecvBufferPT, RecvBuffer);
}
while (Result >= RECVBUFFERLEN);
if (Conn_IsSentQuit(ConnS)) { return; }
conn_parser(ConnS);
}
/* CONN_SEND FUNCTION - JONAS (01.07.2000) */
void conn_send(struct Conn_Struct *ConnS) {
char *SendBufferPT = NULL;
char SendBuffer[SENDBUFFERLEN+1] = "";
unsigned long int SendLen = 0;
unsigned long int SentLen = 0;
signed long int Result = 0;
assert(ConnS != NULL);
assert(Conn_IsSocket(ConnS));
assert(ConnS->FD != FD_NONE);
assert(ConnS->SendBuffer != NULL);
if ((Conn_IsConnectProc(ConnS)) && (Host_IsResolved(ConnS->ResolveFlags))) {
conn_connect(ConnS);
if (!Conn_IsSocket(ConnS)) { return; }
}
for (SendBufferPT = ConnS->SendBuffer ; *SendBufferPT != 0 ; SendBufferPT += SentLen) {
SendLen = strlen(SendBufferPT);
if (SendLen > SENDBUFFERLEN) { SendLen = SENDBUFFERLEN; }
memset(&SendBuffer, 0, sizeof(SendBuffer));
strncpy(SendBuffer, SendBufferPT, SendLen);
Result = send(ConnS->FD, SendBufferPT, SendLen, MSG_NOSIGNAL);
if (Result <= ERROR) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ENOMEM) || (errno == ENOBUFS)) {
unsigned long int Len = 0;
client_noticealluser(ConnS->User, "Connection %s: Write error to %s(%s):%ld: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, errno, strerror(errno));
/*
* EAGAIN/EWOULDBLOCK -- THE SOCKET IMPLEMENTATION CAN'T HANDLE MORE DATA.
* EINTR - INTERRUPTED BY A SIGNAL.
* ENOMEM - NO MEMORY LEFT.
* ENOBUFS - NO BUFFER SPACE AVAILABLE.
*
* COPY WHATS LEFT TO THE THE START OF THE SENDBUFFER, REALLOCATE THE MEMORY AND BAIL OUT - JONAS (01.12.1999)
*
*/
Len = strlen(SendBufferPT) + 1;
memmove(ConnS->SendBuffer, SendBufferPT, Len);
SendBufferPT = realloc(ConnS->SendBuffer, Len);
assert(SendBufferPT != NULL);
ConnS->SendBuffer = SendBufferPT;
return;
}
conn_disconnect(ConnS, "Connection %s: Write error to %s(%s):%ld: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, errno, strerror(errno));
return;
}
SentLen = Result;
assert(SentLen == SendLen);
}
FREE(ConnS->SendBuffer);
}
syntax highlighted by Code2HTML, v. 0.9.1