/* * ---------------------------------------------------------------- * Night Light IRC Proxy - Main 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 (27.11.2007) * */ #define MAIN_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 0 /* 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 0 /* Socket functions */ #define NEED_NETDB_H 0 /* 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" #if MEMDEBUG #include "memlist.h" #endif #include "main.h" #include "conf.h" #if SSL_SUPPORT #include "ssl.h" #endif #include "access_conf.h" #include "listen_conf.h" #include "user_conf.h" #include "conn_conf.h" #include "autoop_conf.h" #include "listen.h" #include "listen_io.h" #include "client.h" #include "client_io.h" #include "client_connection.h" #include "client_handle.h" #include "client_notice.h" #include "conn.h" #include "conn_io.h" #include "conn_connection.h" #if IDENTD #include "ident_conf.h" #include "ident_listen.h" #include "ident_conn.h" #endif /* VARIABLES - JONAS (29.07.2001) */ unsigned long int ScreenBitmask = ( BITMASK_MAIN | BITMASK_ERROR | BITMASK_EXTRA #if DEBUG | BITMASK_DEBUG #define SCREENMODE 1 #endif ); char *STDIN_RecvBufferPT = NULL; #if ARES ares_channel Ares_Channel = { 0 }; #endif extern unsigned short int ScreenMode; extern unsigned short int ScreenPrint; extern unsigned short int Run; extern unsigned short int Exit; extern time_t ExitTime; extern time_t FlushTime; extern struct Conf_Struct ConfS; extern struct Listen_Struct *Listen_Head; extern struct Client_Struct *Client_Head; extern struct Conn_Struct *Conn_Head; #if IDENTD struct IdentListen_Struct *IdentListen_Head; #endif struct PrintFile_Struct PrintFileS[] = { { "/dev/null", 0, NULL }, { MAINLOGFILE, (BITMASK_MAIN|BITMASK_ERROR #if EXTRAINFO |BITMASK_EXTRA #endif ), NULL }, { ERRORLOGFILE, BITMASK_ERROR, NULL }, #if DEBUG { DEBUGLOGFILE, (BITMASK_MAIN|BITMASK_ERROR|BITMASK_DEBUG), NULL } #endif }; unsigned short int PrintFileS_Len = (sizeof(PrintFileS) / sizeof(*PrintFileS)); /* MAIN FUNCTION - JONAS (30.07.2001) */ int main(int argc, char **argv) { signed char Char = 0; #if SCREENMODE unsigned short int Fork = FALSE; #else unsigned short int Fork = TRUE; #endif unsigned short int Term = FALSE; unsigned short int Rehash = FALSE; #if FDDEBUG socktable_add(0, __FILE__, __LINE__, __FUNCTION__); socktable_add(1, __FILE__, __LINE__, __FUNCTION__); socktable_add(2, __FILE__, __LINE__, __FUNCTION__); fdtable_add(stdin, __FILE__, __LINE__, __FUNCTION__, "0", "0"); fdtable_add(stdout, __FILE__, __LINE__, __FUNCTION__, "0", "0"); fdtable_add(stderr, __FILE__, __LINE__, __FUNCTION__, "0", "0"); #endif sysinit(); while (Char != ERROR) { Char = getopt(argc, argv, "vfrtc:"); switch(Char) { case 'v': printf("%s v%s\n", PACKAGE, VERSION); printf("%s\n", COPYRIGHT); exit(0); case 'c': if (optarg != NULL) ConfS.File = strdup(optarg); printf("Using configuration file: %s\n", ConfS.File); break; case 'f': Fork = FALSE; break; case 'r': Rehash = TRUE; break; case 't': Term = TRUE; break; case '?': printf("%s v%s\n", PACKAGE, VERSION); printf("Syntax: %s [-vfcrt] \n", argv[0]); printf("v = Print version\n"); printf("f = Fullscreen mode\n"); printf("c = Specify configuration file\n"); printf("r = Rehash program\n"); printf("t = Terminate program\n"); exit(0); default: break; } } conf_read(); if (Rehash == TRUE) { sysrehash(); exit(0); } if (Term == TRUE) { systerm(); exit(0); } PrintFileS[0].Name = strdup("/dev/null"); PrintFileS[1].Name = strdup(ConfS.MainLogFile); PrintFileS[2].Name = strdup(ConfS.ErrorLogFile); #if DEBUG PrintFileS[3].Name = strdup(ConfS.DebugLogFile); #endif sysinitfiles(); sysopenfiles_fg(); #if !WIN32 sysinitid(); #endif #if !WIN32 syscheckpid(); syswritepid(); if (Fork == TRUE) { sysfork(); } #endif syssetsignals(); sysprint(BITMASK_MAIN, STARTUPLN1); sysprint(BITMASK_MAIN, STARTUPLN2); #if ARES ares_init(&Ares_Channel); #endif #if SSL_SUPPORT if (ConfS.SSLSupport == TRUE) { ssl_init(); } #endif main_read(); main_loop(); assert(FALSE); return(SUCCESS); } /* MAIN_READ FUNCTION - JONAS (25.06.2000) */ void main_read(void) { access_conf_read(); listen_conf_read(); user_conf_read(); conn_conf_read(); #if IDENTD ident_conf_read(); #endif autoop_conf_read(); } /* MAIN_WRITE FUNCTION - JONAS (25.06.2000) */ void main_write(void) { } /* MAIN_REHASH FUNCTION - JONAS (25.06.2000) */ void main_rehash(void) { struct Listen_Struct *ListenS = NULL; struct Client_Struct *ClientS = NULL; struct Conn_Struct *ConnS = NULL; struct ConnServer_Struct *ConnServer = NULL; struct ConnServer_Struct *ConnServer_DEL = NULL; struct ListenConf_Struct *ListenConf = NULL; struct UserConf_Struct *UserConf = NULL; struct ConnConf_Struct *ConnConf = NULL; struct ConnConfServer_Struct *ConnConfServer = NULL; #if IDENTD struct IdentConf_Struct *IdentConfS = NULL; struct IdentListen_Struct *IdentListenS = NULL; #endif main_read(); for (ListenS = Listen_Head ; ListenS != NULL ; ListenS = ListenS->Next) { ListenS->Time = 0; ListenConf = listen_conf_get(ListenS->Host, ListenS->PortH, ((Listen_IsIPv6(ListenS)) ? TRUE : FALSE), ((Listen_IsSSL(ListenS)) ? TRUE : FALSE)); if (ListenConf == NULL) { listen_stop(ListenS); Listen_SetRemove(ListenS); continue; } } for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) { UserConf = user_conf_get(ClientS->User); if (UserConf == NULL) { client_close(ClientS, "User no longer exist after rehashing configuration."); } } for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) { ConnConf = conn_conf_get(ConnS->Name); if (ConnConf == NULL) { if (Conn_IsSocket(ConnS)) { if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Connection %s removed on configuration rehash.", ConnS->Name); } else { conn_disconnect(ConnS, "Connection %s removed on configuration rehash.", ConnS->Name); } } Conn_SetRemove(ConnS); } else { if (strcmp(ConnS->User, ConnConf->User) != FALSE) { for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) { if (ClientS->ConnS == ConnS) { client_notice(ClientS, "You are automatically detached from %s: New owner of connection after rehashing configuration.", ConnS->Name); client_detach(ClientS); } } ConnS->User = strrealloc(ConnS->User, ConnConf->User); } ConnS->Host = strrealloc(ConnS->Host, ConnConf->Host); ConnS->Nick = strrealloc(ConnS->Nick, ConnConf->Nick); ConnS->AwayNick = strrealloc(ConnS->AwayNick, ConnConf->AwayNick); ConnS->Mode = strrealloc(ConnS->Mode, ConnConf->Mode); ConnS->Info = strrealloc(ConnS->Info, ConnConf->Info); ConnS->Chans = strrealloc(ConnS->Chans, ConnConf->Chans); ConnS->ConfFlags = ConnConf->ConfFlags; ConnS->MaxClients = ConnConf->MaxClients; ConnS->SendMaxLines = ConnConf->SendMaxLines; ConnS->SendLineTime = ConnConf->SendLineTime; ConnS->SendMaxBuffer = ConnConf->SendMaxBuffer; ConnS->SendBufferTime = ConnConf->SendBufferTime; ConnS->AwayMsg = strrealloc(ConnS->AwayMsg, ConnConf->AwayMsg); ConnS->PublicDetachMsg = strrealloc(ConnS->PublicDetachMsg, ConnConf->PublicDetachMsg); ConnS->PublicAttachMsg = strrealloc(ConnS->PublicAttachMsg, ConnConf->PublicAttachMsg); ConnS->NickServNUH = strrealloc(ConnS->NickServNUH, ConnConf->NickServNUH); ConnS->NickServPass = strrealloc(ConnS->NickServPass, ConnConf->NickServPass); for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ;) { ConnConfServer = conn_conf_getserver(ConnConf, ConnServer->Host); if (ConnConfServer == NULL) { ConnServer_DEL = ConnServer; ConnServer = ConnServer->Next; conn_remserver(ConnS, ConnServer_DEL); continue; } else { ConnServer->Host = strrealloc(ConnServer->Host, ConnConfServer->Host); ConnServer->Port = ConnConfServer->Port; ConnServer = ConnServer->Next; continue; } } } } #if IDENTD for (IdentListenS = IdentListen_Head ; IdentListenS != NULL ; IdentListenS = IdentListenS->Next) { IdentConfS = ident_conf_get(IdentListenS->Host, IdentListenS->PortH); if (IdentConfS == NULL) { ident_listen_stop(IdentListenS); IdentListen_SetRemove(IdentListenS); continue; } } #endif } /* MAIN_LOOP FUNCTION - JONAS (29.07.2001) */ void main_loop(void) { signed long int Result = 0; unsigned long int FDS = 0; time_t Duration = 0; fd_set ReadFDS; fd_set WriteFDS; struct timeval TV = { 0 }; FOREVERLOOP { NOW = time(NULL); Duration = (NOW - FlushTime); if (Duration >= FLUSHTIME) { sysflushfiles(); } assert(geteuid() != 0); if (Exit == TRUE) { TV.tv_sec = 1; } else { TV.tv_sec = NLPRGSEC; } TV.tv_usec = NLPRGUSEC; FD_ZERO(&ReadFDS); FD_ZERO(&WriteFDS); if (ScreenMode == TRUE) { FD_SET(STDIN_FILENO, &ReadFDS); } if (Run == TRUE) { #if ARES ares_fds(Ares_Channel, &ReadFDS, &WriteFDS); #endif listen_fds(&ReadFDS, &WriteFDS, &FDS); client_fds(&ReadFDS, &WriteFDS, &FDS); conn_fds(&ReadFDS, &WriteFDS, &FDS); #if IDENTD ident_listen_fds(&ReadFDS, &WriteFDS, &FDS); ident_conn_fds(&ReadFDS, &WriteFDS, &FDS); #endif } Result = select(FD_SETSIZE, &ReadFDS, &WriteFDS, NULL, &TV); if (Result <= ERROR) { if (errno == EINTR) { continue; } sysprint(BITMASK_ERROR, "select() I/O error: [%d]: %s", errno, strerror(errno)); exit(1); } FDS = Result; NOW = time(NULL); if (ScreenMode == TRUE) { main_io(&ReadFDS, &WriteFDS, &FDS); } if (Run == TRUE) { #if ARES ares_process(Ares_Channel, &ReadFDS, &WriteFDS); #endif listen_io(&ReadFDS, &WriteFDS, &FDS); client_io(&ReadFDS, &WriteFDS, &FDS); conn_io(&ReadFDS, &WriteFDS, &FDS); #if IDENTD ident_listen_io(&ReadFDS, &WriteFDS, &FDS); ident_conn_io(&ReadFDS, &WriteFDS, &FDS); #endif } #if 0 /* Doesn't work with C-Ares */ assert(FDS == 0); #endif if (Exit == TRUE) { main_exit("Exit."); } } } /* MAIN_IO FUNCTION - JONAS (01.03.2000) */ static void main_io(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) { signed long int Result = 0; unsigned long int Len = 0; unsigned long int OldLen = 0; unsigned long int NewLen = 0; char RecvBuffer[RECVBUFFERLEN+1] = ""; char *RecvBufferPT = NULL; char *TempPT = NULL; if (!FD_ISSET(STDIN_FILENO, ReadFDS)) { return; } *FDS = *FDS - 1; do { memset(&RecvBuffer, 0, RECVBUFFERLEN+1); Result = read(STDIN_FILENO, RecvBuffer, RECVBUFFERLEN); if (Result <= ERROR) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { break; } sysprint(BITMASK_ERROR, "Read error to stdin: [%d]: %s", errno, strerror(errno)); exit(1); } Len = Result; if (Len == 0) { sysprint(BITMASK_ERROR, "EOF to stdin."); exit(1); } if (Len <= 1) { return; } assert(Len == strlen(RecvBuffer)); if (RecvBufferPT == NULL) { OldLen = 0; } else { OldLen = strlen(RecvBufferPT); } NewLen = OldLen + Len + 1; TempPT = realloc(RecvBufferPT, NewLen); if (TempPT == NULL) { exit(1); } RecvBufferPT = TempPT; TempPT += OldLen; strcpy(TempPT, RecvBuffer); } while (Len >= RECVBUFFERLEN); STDIN_RecvBufferPT = RecvBufferPT; main_parse(); } /* MAIN_PARSE FUNCTION - JONAS (01.07.2000) */ static void main_parse(void) { char *BufferPT = NULL; char *LinePT = NULL; char *DumbPT = NULL; unsigned long int Count = 0; unsigned long int Len = 0; assert(STDIN_RecvBufferPT != NULL); BufferPT = STDIN_RecvBufferPT; for (Count = 1 ; BufferPT != NULL ; Count++) { DumbPT = strchr(BufferPT, '\n'); if (DumbPT == NULL) { if (Count == 1) { return; } BufferPT = strtok(BufferPT, "\0"); if (BufferPT != NULL) { Len = strlen(BufferPT) + 1; strcpy(STDIN_RecvBufferPT, BufferPT); DumbPT = realloc(STDIN_RecvBufferPT, Len); if (DumbPT == NULL) { exit(1); } STDIN_RecvBufferPT = DumbPT; return; } FREE(STDIN_RecvBufferPT); return; } LinePT = strtok(BufferPT, "\n"); BufferPT = strtok(NULL, "\0"); if (LinePT == NULL) { continue; } Len = strlen(LinePT); if (LinePT[Len-1] == '\r') { LinePT[Len-1] = 0; } if (strcasecmp(LinePT, "START") == FALSE) { if (Run == TRUE) { printf("I/O loop already started.\n"); continue; } Run = TRUE; printf("I/O loop started.\n"); } else if (strcasecmp(LinePT, "STOP") == FALSE) { if (Run == FALSE) { printf("I/O loop already stopped.\n"); continue; } Run = FALSE; printf("I/O loop stopped.\n"); } #if !WIN32 else if (strcasecmp(LinePT, "FORK") == FALSE) { printf("Going into the background...\n"); sysfork(); continue; } #endif else if (strcasecmp(LinePT, "REHASH") == FALSE) { printf("Rehashing configuration...\n"); main_rehash(); continue; } else if (strcasecmp(LinePT, "EXIT") == FALSE) { main_exit("EXIT from console."); continue; } else if (strcasecmp(LinePT, "EXITNOW") == FALSE) { exit(0); } #if MEMDEBUG else if (strcasecmp(LinePT, "MEMLIST") == FALSE) { mem_list(); continue; } else if (strcasecmp(LinePT, "MEMLEAKS") == FALSE) { mem_leaks(); continue; } #endif #if FDDEBUG else if (strcasecmp(LinePT, "FDPRINT") == FALSE) { fd_print(); continue; } #endif else if (strcasecmp(LinePT, "PRINT1") == FALSE) { sysprint(BITMASK_MAIN, "This is a test of MAIN print."); continue; } else if (strcasecmp(LinePT, "PRINT2") == FALSE) { sysprint(BITMASK_ERROR, "This is a test of ERROR print."); continue; } else if (strcasecmp(LinePT, "PRINT3") == FALSE) { continue; } else { printf("Unknown command.\n"); continue; } if (STDIN_RecvBufferPT == NULL) { break; } } FREE(STDIN_RecvBufferPT); } /* MAIN_EXIT FUNCTION - JONAS (01.07.2000) */ void main_exit(const char *const MessagePT, ...) { char Message[LINELEN+1] = ""; va_list Args = { 0 }; unsigned short int Count = 0; time_t Duration = 0; va_start(Args, MessagePT); vsnprintf(Message, LINELEN+1, MessagePT, Args); va_end(Args); if (Exit == FALSE) { DEBUGPRINT(BITMASK_DEBUG_MAIN, "Shutting down: %s", Message); client_noticeall("Shutting down: %s", Message); Exit = TRUE; ExitTime = NOW; } Count += client_closeall("%s", Message); Count += conn_closeall("%s", Message); Count += listen_closeall("%s", Message); #if IDENTD Count += ident_listen_closeall("%s", Message); Count += ident_conn_closeall(); #endif #if ARES && !HAVE_ARES_CANCELQUERY && HAVE_ARES_CANCEL ares_cancel(Ares_Channel); #endif Duration = (NOW - ExitTime); if ((Count == 0) || (Exit >= EXITTTL)) { access_conf_destroy(); listen_conf_destroy(); user_conf_destroy(); conn_conf_destroy(); #if IDENTD ident_conf_destroy(); #endif autoop_conf_destroy(); conf_destroy(); #if ARES ares_destroy(Ares_Channel); #endif #if SSL_SUPPORT ssl_cleanup(); #endif irc_cleanup(); strcleanup(); syscleanup(); #if FDDEBUG fd_print(); fdtable_clear(); socktable_clear(); #endif #if MEMDEBUG mem_list(); mem_leaks(); #endif exit(0); } }