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