コード例 #1
0
ファイル: IRCConnection.cpp プロジェクト: Ze0de/shroudbnc
/**
 * IRCPingTimer
 *
 * Checks the IRC connection for timeouts.
 *
 * @param Now the current time
 * @param IRCConnection the CIRCConnection object
 */
bool IRCPingTimer(time_t Now, void *IRCConnection) {
	CIRCConnection *IRC = (CIRCConnection *)IRCConnection;

	if (IRC->GetSocket() == INVALID_SOCKET) {
		return true;
	}

	if (g_CurrentTime - IRC->m_LastResponse > 300) {
		const char *ServerName;
		
		ServerName = IRC->GetServer();

		if (ServerName == NULL) {
			ServerName = "sbnc";
		}

		IRC->WriteLine("PING :%s", ServerName);
		IRC->m_EatPong = true;

		/* we're using "Now" here, because that's the time when the
		 * check should've returned a PONG, this might be useful when
		 * we're in a time warp */
		if (Now - IRC->m_LastResponse > 600) {
			IRC->Kill("Server does not respond.");
		}
	}

	return true;
}
コード例 #2
0
ファイル: Core.cpp プロジェクト: demize/shroudbnc
/**
 * StartMainLoop
 *
 * Executes shroudBNC's main loop.
 */
void CCore::StartMainLoop(bool ShouldDaemonize) {
	unsigned int i;

	time(&g_CurrentTime);

	int Port = CacheGetInteger(m_ConfigCache, port);
#ifdef HAVE_LIBSSL
	int SSLPort = CacheGetInteger(m_ConfigCache, sslport);

	if (Port == 0 && SSLPort == 0) {
#else
	if (Port == 0) {
#endif
		Port = 9000;
	}

	const char *BindIp = CacheGetString(m_ConfigCache, ip);

	if (m_Listener == NULL) {
		if (Port != 0) {
			m_Listener = new CClientListener(Port, BindIp, AF_INET);
		} else {
			m_Listener = NULL;
		}
	}

	if (m_ListenerV6 == NULL) {
		if (Port != 0) {
			m_ListenerV6 = new CClientListener(Port, BindIp, AF_INET6);

			if (m_ListenerV6->IsValid() == false) {
				delete m_ListenerV6;

				m_ListenerV6 = NULL;
			}
		} else {
			m_ListenerV6 = NULL;
		}
	}

#ifdef HAVE_LIBSSL
	if (m_SSLListener == NULL) {
		if (SSLPort != 0) {
			m_SSLListener = new CClientListener(SSLPort, BindIp, AF_INET, true);
		} else {
			m_SSLListener = NULL;
		}
	}

	if (m_SSLListenerV6 == NULL) {
		if (SSLPort != 0) {
			m_SSLListenerV6 = new CClientListener(SSLPort, BindIp, AF_INET6, true);

			if (m_SSLListenerV6->IsValid() == false) {
				delete m_SSLListenerV6;

				m_SSLListenerV6 = NULL;
			}
		} else {
			m_SSLListenerV6 = NULL;
		}
	}

	SSL_library_init();
	SSL_load_error_strings();

	SSL_METHOD *SSLMethod = (SSL_METHOD *)SSLv23_method();

	m_SSLContext = SSL_CTX_new(SSLMethod);
	SSL_CTX_set_passwd_cb(m_SSLContext);

	m_SSLClientContext = SSL_CTX_new(SSLMethod);
	SSL_CTX_set_passwd_cb(m_SSLClientContext);

	SSL_CTX_set_mode(m_SSLContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
	SSL_CTX_set_mode(m_SSLClientContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

	g_SSLCustomIndex = SSL_get_ex_new_index(0, (void *)"CConnection*", NULL, NULL, NULL);

	if (!SSL_CTX_use_PrivateKey_file(m_SSLContext, BuildPathConfig("sbnc.key"), SSL_FILETYPE_PEM)) {
		if (SSLPort != 0) {
			Log("Could not load private key (sbnc.key).");
			ERR_print_errors_fp(stdout);
			return;
		} else {
			SSL_CTX_free(m_SSLContext);
			m_SSLContext = NULL;
		}
	} else {
		SSL_CTX_set_verify(m_SSLContext, SSL_VERIFY_PEER, SSLVerifyCertificate);
	}

	if (!SSL_CTX_use_certificate_chain_file(m_SSLContext, BuildPathConfig("sbnc.crt"))) {
		if (SSLPort != 0) {
			Log("Could not load public key (sbnc.crt).");
			ERR_print_errors_fp(stdout);
			return;
		} else {
			SSL_CTX_free(m_SSLContext);
			m_SSLContext = NULL;
		}
	} else {
		SSL_CTX_set_verify(m_SSLClientContext, SSL_VERIFY_PEER, SSLVerifyCertificate);
	}
#endif

	if (Port != 0 && m_Listener != NULL && m_Listener->IsValid()) {
		Log("Created main listener.");
	} else if (Port != 0) {
		Log("Could not create listener port");
		return;
	}

#ifdef HAVE_LIBSSL
	if (SSLPort != 0 && m_SSLListener != NULL && m_SSLListener->IsValid()) {
		Log("Created ssl listener.");
	} else if (SSLPort != 0) {
		Log("Could not create ssl listener port");
		return;
	}
#endif

	InitializeAdditionalListeners();

	Log("Starting main loop.");

	if (ShouldDaemonize) {
#ifndef _WIN32
		fprintf(stderr, "Daemonizing... ");

		UnlockPidFile();

		if (Daemonize()) {
			fprintf(stderr, "DONE\n");
		} else {
			fprintf(stderr, "FAILED\n");
		}

		WritePidFile();
#endif
	}

	/* Note: We need to load the modules after using fork() as otherwise tcl cannot be cleanly unloaded */
	m_LoadingModules = true;

#ifdef _MSC_VER
	LoadModule("bnctcl.dll");
    LoadModule("bncidentd.dll");
#else
	LoadModule("libbnctcl.la");
#endif

	char *Out;

	i = 0;
	while (true) {
		int rc = asprintf(&Out, "system.modules.mod%d", i++);

		if (RcFailed(rc)) {
			Fatal();
		}

		const char *File = m_Config->ReadString(Out);

		free(Out);

		if (File == NULL) {
			break;
		}

		LoadModule(File);
	}

	m_LoadingModules = false;

	i = 0;
	while (hash_t<CUser *> *User = m_Users.Iterate(i++)) {
		User->Value->LoadEvent();
	}

	int m_ShutdownLoop = 5;

	time_t Last = 0;

	while (GetStatus() == Status_Running || --m_ShutdownLoop) {
		time_t Now, Best = 0, SleepInterval = 0;

#if defined(_WIN32) && defined(_DEBUG)
		DWORD TickCount = GetTickCount();
#endif

		time(&Now);

		i = 0;
		while (hash_t<CUser *> *UserHash = m_Users.Iterate(i++)) {
			CIRCConnection *IRC;

			if ((IRC = UserHash->Value->GetIRCConnection()) != NULL) {
				if (GetStatus() != Status_Running) {
					Log("Closing connection for user %s", UserHash->Name);
					IRC->Kill("Shutting down.");

					UserHash->Value->SetIRCConnection(NULL);
				}

				if (IRC->ShouldDestroy()) {
					IRC->Destroy();
				}
			}
		}

		CUser::RescheduleReconnectTimer();

		time(&Now);

		if (g_CurrentTime - 5 > Now) {
			Log("Time warp detected: %d seconds", Now - g_CurrentTime);
		}

		g_CurrentTime = Now;

		Best = CTimer::GetNextCall();

		if (Best <= g_CurrentTime) {
#ifdef _DEBUG
			if (g_CurrentTime - 1 > Best) {
#else
			if (g_CurrentTime - 5 > Best) {
#endif
				Log("Time warp detected: %d seconds", g_CurrentTime - Best);
			}

			CTimer::CallTimers();

			Best = CTimer::GetNextCall();
		}

		SleepInterval = Best - g_CurrentTime;

		DnsSocketCookie *DnsCookie = CDnsQuery::RegisterSockets();

		for (CListCursor<socket_t> SocketCursor(&m_OtherSockets); SocketCursor.IsValid(); SocketCursor.Proceed()) {
			if (SocketCursor->PollFd->fd == INVALID_SOCKET) {
				continue;
			}

			if (SocketCursor->Events->ShouldDestroy()) {
				SocketCursor->Events->Destroy();
			} else {
				SocketCursor->PollFd->events = POLLIN | POLLERR;

				if (SocketCursor->Events->HasQueuedData()) {
					SocketCursor->PollFd->events |= POLLOUT;
				}
			}
		}

		bool ModulesBusy = false;

	        for (int j = 0; j < m_Modules.GetLength(); j++) {
        	        if (m_Modules[j]->MainLoop()) {
                	        ModulesBusy = true;
	                }
	        }

		if (SleepInterval <= 0 || GetStatus() != Status_Running || ModulesBusy) {
			SleepInterval = 1;
		}

		if (GetStatus() != Status_Running && SleepInterval > 3) {
			SleepInterval = 3;
		}

		timeval interval = { (long)SleepInterval, 0 };

		time(&Last);

#ifdef _DEBUG
		//printf("poll: %d seconds\n", SleepInterval);
#endif

#if defined(_WIN32) && defined(_DEBUG)
		DWORD TimeDiff = GetTickCount();
#endif

		int ready = poll(m_PollFds.GetList(), m_PollFds.GetLength(), interval.tv_sec * 1000);

#if defined(_WIN32) && defined(_DEBUG)
		TickCount += GetTickCount() - TimeDiff;
#endif

		time(&g_CurrentTime);

		if (ready > 0) {
			for (CListCursor<socket_t> SocketCursor(&m_OtherSockets); SocketCursor.IsValid(); SocketCursor.Proceed()) {
				pollfd *PollFd = SocketCursor->PollFd;
				CSocketEvents *Events = SocketCursor->Events;

				if (PollFd->fd != INVALID_SOCKET) {
					if (PollFd->revents & (POLLERR|POLLHUP|POLLNVAL)) {
						int ErrorCode;
						socklen_t ErrorCodeLength = sizeof(ErrorCode);

						ErrorCode = 0;

						if (getsockopt(PollFd->fd, SOL_SOCKET, SO_ERROR, (char *)&ErrorCode, &ErrorCodeLength) != -1) {
							if (ErrorCode != 0) {
								Events->Error(ErrorCode);
							}
						}

						if (ErrorCode == 0) {
							Events->Error(-1);
						}

						Events->Destroy();

						continue;
					}

					if (PollFd->revents & (POLLIN|POLLPRI)) {
						int Code;
						if ((Code = Events->Read()) != 0) {
							Events->Error(Code);
							Events->Destroy();

							continue;
						}
					}

					if (PollFd->revents & POLLOUT) {
						Events->Write();
					}
				}
			}
		} else if (ready == -1) {
#ifndef _WIN32
			if (errno != EBADF && errno != 0) {
#else
			if (errno != WSAENOTSOCK) {
#endif
				continue;
			}

			m_OtherSockets.Lock();

			for (CListCursor<socket_t> SocketCursor(&m_OtherSockets); SocketCursor.IsValid(); SocketCursor.Proceed()) {
				if (SocketCursor->PollFd->fd == INVALID_SOCKET) {
					continue;
				}

				pollfd pfd;
				pfd.fd = SocketCursor->PollFd->fd;
				pfd.events = POLLIN | POLLOUT | POLLERR;

				int code = poll(&pfd, 1, 0);

				if (code == -1) {
					SocketCursor->Events->Error(-1);
					SocketCursor->Events->Destroy();
				}
			}
		}

		CDnsQuery::ProcessTimeouts();
		CDnsQuery::UnregisterSockets(DnsCookie);

#if defined(_WIN32) && defined(_DEBUG)
		DWORD Ticks = GetTickCount() - TickCount;

		if (Ticks > 50) {
			printf("Spent %d msec in the main loop.\n", Ticks);
		}
#endif
	}

#ifdef HAVE_LIBSSL
	SSL_CTX_free(m_SSLContext);
	SSL_CTX_free(m_SSLClientContext);
#endif
}

/**
 * GetUser
 *
 * Returns a user object for the specified username (or NULL
 * if there's no such user).
 *
 * @param Name the username
 */
CUser *CCore::GetUser(const char *Name) {
	if (Name == NULL) {
		return NULL;
	} else {
		return m_Users.Get(Name);
	}
}

/**
 * GlobalNotice
 *
 * Sends a message to all bouncer users who are currently
 * logged in.
 *
 * @param Text the text of the message
 */
void CCore::GlobalNotice(const char *Text) {
	int i = 0;
	char *GlobalText;

	int rc = asprintf(&GlobalText, "Global admin message: %s", Text);

	if (RcFailed(rc)) {
		return;
	}

	while (hash_t<CUser *> *User = m_Users.Iterate(i++)) {
		if (User->Value->GetClientConnectionMultiplexer() != NULL) {
			User->Value->GetClientConnectionMultiplexer()->Privmsg(GlobalText);
		} else {
			User->Value->Log("%s", GlobalText);
		}
	}

	free(GlobalText);
}

/**
 * GetUsers
 *
 * Returns a hashtable which contains all bouncer users.
 */
CHashtable<CUser *, false> *CCore::GetUsers(void) {
	return &m_Users;
}

/**
 * SetIdent
 *
 * Sets the ident which is returned for the next ident request.
 *
 * @param Ident the ident
 */
void CCore::SetIdent(const char *Ident) {
	if (m_Ident != NULL) {
		m_Ident->SetIdent(Ident);
	}
}

/**
 * GetIdent
 *
 * Returns the ident which is to be returned for the next ident
 * request.
 */
const char *CCore::GetIdent(void) const {
	if (m_Ident != NULL) {
		return m_Ident->GetIdent();
	} else {
		return NULL;
	}
}

/**
 * GetModules
 *
 * Returns a list of currently loaded modules.
 */
const CVector<CModule *> *CCore::GetModules(void) const {
	return &m_Modules;
}

/**
 * LoadModule
 *
 * Attempts to load a bouncer module.
 *
 * @param Filename the module's filename
 */
RESULT<CModule *> CCore::LoadModule(const char *Filename) {
	RESULT<bool> Result;

	CModule *Module = new CModule(Filename);

	if (AllocFailed(Module)) {
		THROW(CModule *, Generic_OutOfMemory, "new operator failed.");
	}

	Result = Module->GetError();

	if (!IsError(Result)) {
		Result = m_Modules.Insert(Module);

		if (IsError(Result)) {
			delete Module;

			Log("Insert() failed. Could not load module");

			THROWRESULT(CModule *, Result);
		}

		Log("Loaded module: %s", Module->GetFilename());

		Module->Init(this);

		if (!m_LoadingModules) {
			UpdateModuleConfig();
		}

		RETURN(CModule *, Module);
	} else {
		static char *ErrorString = NULL;

		free(ErrorString);

		ErrorString = strdup(GETDESCRIPTION(Result));

		if (AllocFailed(ErrorString)) {
			delete Module;

			THROW(CModule *, Generic_OutOfMemory, "strdup() failed.");
		}

		Log("Module %s could not be loaded: %s", Filename, ErrorString);

		delete Module;

		THROW(CModule *, Generic_Unknown, ErrorString);
	}

	THROW(CModule *, Generic_Unknown, NULL);
}

/**
 * UnloadModule
 *
 * Unloads a module.
 *
 * @param Module the module
 */
bool CCore::UnloadModule(CModule *Module) {
	if (m_Modules.Remove(Module)) {
		Log("Unloaded module: %s", Module->GetFilename());

		delete Module;

		UpdateModuleConfig();

		return true;
	} else {
		return false;
	}
}

/**
 * UpdateModuleConfig
 *
 * Updates the module list in the main config.
 */
void CCore::UpdateModuleConfig(void) {
	char *Out;
	int a = 0, rc;

	for (int i = 0; i < m_Modules.GetLength(); i++) {
		rc = asprintf(&Out, "system.modules.mod%d", a++);

		if (RcFailed(rc)) {
			Fatal();
		}

		m_Config->WriteString(Out, m_Modules[i]->GetFilename());

		free(Out);
	}

	rc = asprintf(&Out, "system.modules.mod%d", a);

	if (RcFailed(rc)) {
		Fatal();
	}

	m_Config->WriteString(Out, NULL);

	free(Out);
}