diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.cpp | 3 | ||||
-rw-r--r-- | src/dns.cpp | 23 | ||||
-rw-r--r-- | src/init.cpp | 1 | ||||
-rw-r--r-- | src/main.cpp | 444 | ||||
-rw-r--r-- | src/messages.cpp | 2 | ||||
-rw-r--r-- | src/misc.cpp | 9 | ||||
-rw-r--r-- | src/modulemanager.cpp | 8 | ||||
-rw-r--r-- | src/socketengines/socketengine_epoll.cpp | 2 | ||||
-rw-r--r-- | src/socketengines/socketengine_poll.cpp | 2 | ||||
-rw-r--r-- | src/socketengines/socketengine_select.cpp | 2 | ||||
-rw-r--r-- | src/sockets.cpp | 132 |
11 files changed, 327 insertions, 301 deletions
diff --git a/src/config.cpp b/src/config.cpp index 671cf3ed7..7d3efcb91 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -563,7 +563,7 @@ bool InitUplinks(ServerConfig *config, const Anope::string &) { if (!config->Uplinks.empty()) { - std::list<Uplink *>::iterator curr_uplink = config->Uplinks.begin(), end_uplink = config->Uplinks.end(); + std::vector<Uplink *>::iterator curr_uplink = config->Uplinks.begin(), end_uplink = config->Uplinks.end(); for (; curr_uplink != end_uplink; ++curr_uplink) delete *curr_uplink; } @@ -1033,7 +1033,6 @@ ConfigItems::ConfigItems(ServerConfig *conf) {"options", "mlock", "+nrt", new ValueContainerString(&conf->MLock), DT_STRING, NoValidation}, {"options", "nomlock", "", new ValueContainerString(&conf->NoMLock), DT_STRING, NoValidation}, {"options", "botmodes", "", new ValueContainerString(&conf->BotModes), DT_STRING, NoValidation}, - {"options", "maxretries", "10", new ValueContainerUInt(&conf->MaxRetries), DT_UINTEGER, NoValidation}, {"options", "retrywait", "60", new ValueContainerInt(&conf->RetryWait), DT_INTEGER, ValidateNotZero}, {"options", "hideprivilegedcommands", "no", new ValueContainerBool(&conf->HidePrivilegedCommands), DT_BOOLEAN, NoValidation}, {"nickserv", "nick", "NickServ", new ValueContainerString(&conf->s_NickServ), DT_STRING | DT_NORELOAD, ValidateNotEmpty}, diff --git a/src/dns.cpp b/src/dns.cpp index 7475545c0..4c1178865 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -42,6 +42,11 @@ void DNSRequest::Process() { Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->address << ", of type " << this->QT; + if (!DNSEngine) + throw SocketException("DNSEngine has not been initialized"); + else if (!DNSEngine->sock || !DNSEngine->sock->connected) + throw SocketException("Connection to nameserver has not been established"); + if (this->use_cache && DNSEngine->CheckCache(this)) { Log(LOG_DEBUG_2) << "Resolver: Using cached result"; @@ -236,7 +241,8 @@ DNSSocket::~DNSSocket() delete DNSEngine->packets[i - 1]; DNSEngine->packets.clear(); Log(LOG_NORMAL, "dns") << "Resolver: Lost connection to nameserver"; - DNSEngine->sock = NULL; + if (DNSEngine) + DNSEngine->sock = NULL; } int DNSSocket::SendTo(const unsigned char *buf, size_t len) const @@ -497,7 +503,10 @@ bool DNSSocket::ProcessRead() bool DNSSocket::ProcessWrite() { - Log(LOG_DEBUG_2) << "Resolver: Writing to UDP socket"; + if (!this->connected) + return ConnectionSocket::ProcessWrite(); + + Log(LOG_DEBUG_2) << "Resolver: Writing to DNS socket"; bool cont = true; for (unsigned i = DNSEngine->packets.size(); cont && i > 0; --i) @@ -517,6 +526,16 @@ bool DNSSocket::ProcessWrite() return cont; } +void DNSSocket::OnConnect() +{ + Log(LOG_DEBUG_2) << "Resolver: Successfully connected to nameserver"; +} + +void DNSSocket::OnError(const Anope::string &error) +{ + Log() << "Resolver: Error connecting to nameserver: " << error; +} + DNSManager::DNSManager() : Timer(300, Anope::CurTime, true) { this->sock = NULL; diff --git a/src/init.cpp b/src/init.cpp index ad95e4d4b..7fa684336 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -376,7 +376,6 @@ void Init(int ac, char **av) /* Announce ourselves to the logfile. */ Log() << "Anope " << Anope::Version() << " starting up" << (debug || readonly ? " (options:" : "") << (debug ? " debug" : "") << (readonly ? " readonly" : "") << (debug || readonly ? ")" : ""); - start_time = Anope::CurTime; /* Set signal handlers. Catch certain signals to let us do things or * panic as necessary, and ignore all others. diff --git a/src/main.cpp b/src/main.cpp index 380240dbb..199b3162f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,8 +55,8 @@ Anope::string binary_dir; /* Used to store base path for Anope */ /* Set to 1 if we are to quit */ bool quitting = false; -/* Set to 1 if we are to quit after saving databases */ -bool shutting_down = false; +/* Set to true if we are restarting */ +bool restarting = false; /* Contains a message as to why services is terminating */ Anope::string quitmsg; @@ -65,10 +65,7 @@ Anope::string quitmsg; bool save_data = false; /* At what time were we started? */ -time_t start_time; - -/* Parameters and environment */ -char **my_av, **my_envp; +time_t start_time = time(NULL); time_t Anope::CurTime = time(NULL); @@ -92,123 +89,125 @@ class UpdateTimer : public Timer }; ConnectionSocket *UplinkSock = NULL; +int CurrentUplink = 0; -UplinkSocket::UplinkSocket(bool ipv6) : ConnectionSocket(ipv6) -{ - UplinkSock = this; -} +static void Connect(); -UplinkSocket::~UplinkSocket() +class ReconnectTimer : public Timer { - SocketEngine::Process(); - UplinkSock = NULL; -} + public: + ReconnectTimer(int wait) : Timer(wait) { } -bool UplinkSocket::Read(const Anope::string &buf) + void Tick(time_t) + { + Connect(); + } +}; + +class UplinkSocket : public ConnectionSocket { - process(buf); - return true; -} + public: + UplinkSocket() : ConnectionSocket(Config->Uplinks[CurrentUplink]->ipv6) + { + UplinkSock = this; + } -/*************************************************************************/ + ~UplinkSocket() + { + if (Me && Me->GetUplink() && Me->GetUplink()->IsSynced()) + { + FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect()); -void save_databases() -{ - if (readonly) - return; + /* Send a quit for all of our bots */ + for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + /* Don't use quitmsg here, it may contain information you don't want people to see */ + ircdproto->SendQuit(bi, "Shutting down"); + /* Erase bots from the user list so they don't get nuked later on */ + UserListByNick.erase(bi->nick); + if (!bi->GetUID().empty()) + UserListByUID.erase(bi->GetUID()); + } - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase()); - Log(LOG_DEBUG) << "Saving databases"; -} + /* Clear all of our users, but not our bots */ + for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *u = it->second; + ++it; -/*************************************************************************/ + if (u->server != Me) + delete u; + } -/* Restarts services */ -void do_restart_services() -{ - if (!readonly) - { - save_databases(); - } - Log() << "Restarting"; + ircdproto->SendSquit(Config->ServerName, quitmsg); - if (quitmsg.empty()) - quitmsg = "Restarting"; + this->ProcessWrite(); // Write out the last bit + } - FOREACH_MOD(I_OnRestart, OnRestart()); - ModuleManager::UnloadAll(); + Me->SetFlag(SERVER_SYNCING); + for (unsigned i = Me->GetLinks().size(); i > 0; --i) + if (!Me->GetLinks()[i - 1]->HasFlag(SERVER_JUPED)) + delete Me->GetLinks()[i - 1]; - /* Send a quit for all of our bots */ - for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - /* Don't use quitmsg here, it may contain information you don't want people to see */ - ircdproto->SendQuit(bi, "Restarting"); - /* Erase bots from the user list so they don't get nuked later on */ - UserListByNick.erase(bi->nick); - if (!bi->GetUID().empty()) - UserListByUID.erase(bi->GetUID()); - } + UplinkSock = NULL; - ircdproto->SendSquit(Config->ServerName, quitmsg); - delete UplinkSock; - SocketEngine::Shutdown(); + if (!quitting) + { + int Retry = Config->RetryWait; + if (Retry <= 0) + Retry = 60; - chdir(binary_dir.c_str()); - my_av[0] = const_cast<char *>(("./" + services_bin).c_str()); - execve(services_bin.c_str(), my_av, my_envp); - if (!readonly) - { - throw FatalException("Restart failed"); + Log() << "Retrying in " << Retry << " seconds"; + new ReconnectTimer(Retry); + } } - exit(1); -} + bool Read(const Anope::string &buf) + { + process(buf); + return true; + } -/*************************************************************************/ + void OnConnect() + { + Log() << "Successfully connected to " << Config->Uplinks[CurrentUplink]->host << ":" << Config->Uplinks[CurrentUplink]->port; + ircdproto->SendConnect(); + FOREACH_MOD(I_OnServerConnect, OnServerConnect()); + } -/* Terminates services */ + void OnError(const Anope::string &error) + { + Log() << "Unable to connect to server " << Config->Uplinks[CurrentUplink]->host << ":" << Config->Uplinks[CurrentUplink]->port << (!error.empty() ? (": " + error) : ""); + this->SetFlag(SF_DEAD); + } +}; -static void services_shutdown() +static void Connect() { - if (quitmsg.empty()) - quitmsg = "Terminating, reason unknown"; - Log() << quitmsg; + if (static_cast<unsigned>(++CurrentUplink) >= Config->Uplinks.size()) + CurrentUplink = 0; - FOREACH_MOD(I_OnShutdown, OnShutdown()); - ModuleManager::UnloadAll(); + Uplink *u = Config->Uplinks[CurrentUplink]; - if (started && UplinkSock) - { - /* Send a quit for all of our bots */ - for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - /* Don't use quitmsg here, it may contain information you don't want people to see */ - ircdproto->SendQuit(bi, "Shutting down"); - /* Erase bots from the user list so they don't get nuked later on */ - UserListByNick.erase(bi->nick); - if (!bi->GetUID().empty()) - UserListByUID.erase(bi->GetUID()); - } + new UplinkSocket(); + if (!Config->LocalHost.empty()) + UplinkSock->Bind(Config->LocalHost); + FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect()); + UplinkSock->Connect(u->host, u->port); +} - ircdproto->SendSquit(Config->ServerName, quitmsg); +/*************************************************************************/ - for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) - { - User *u = it->second; - ++it; - delete u; - } - } - delete UplinkSock; - SocketEngine::Shutdown(); +void save_databases() +{ + if (readonly) + return; - /* just in case they weren't all removed at least run once */ - ModuleManager::CleanupRuntimeDirectory(); + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase()); + Log(LOG_DEBUG) << "Saving databases"; } /*************************************************************************/ @@ -314,201 +313,132 @@ Anope::string GetFullProgDir(const Anope::string &argv0) /*************************************************************************/ -static bool Connect() -{ - /* Connect to the remote server */ - int servernum = 1; - for (std::list<Uplink *>::iterator curr_uplink = Config->Uplinks.begin(), end_uplink = Config->Uplinks.end(); curr_uplink != end_uplink; ++curr_uplink, ++servernum) - { - uplink_server = *curr_uplink; - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnPreServerConnect, OnPreServerConnect(*curr_uplink, servernum)); - if (MOD_RESULT != EVENT_CONTINUE) - { - if (MOD_RESULT == EVENT_STOP) - continue; - return true; - } - - DNSRecord req = DNSManager::BlockingQuery(uplink_server->host, uplink_server->ipv6 ? DNS_QUERY_AAAA : DNS_QUERY_A); - - if (!req) - { - Log() << "Unable to connect to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "): Invalid hostname/IP"; - continue; - } - - try - { - new UplinkSocket(uplink_server->ipv6); - UplinkSock->Connect(req.result, uplink_server->port, Config->LocalHost); - } - catch (const SocketException &ex) - { - Log() << "Unable to connect to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "): " << ex.GetReason(); - continue; - } - - Log() << "Connected to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << ")"; - return true; - } - - uplink_server = NULL; - - return false; -} - -/*************************************************************************/ - /* Main routine. (What does it look like? :-) ) */ int main(int ac, char **av, char **envp) { - try - { - my_av = av; - my_envp = envp; + int return_code = 0; - char cwd[PATH_MAX] = ""; + char cwd[PATH_MAX] = ""; #ifdef _WIN32 - GetCurrentDirectory(PATH_MAX, cwd); + GetCurrentDirectory(PATH_MAX, cwd); #else - getcwd(cwd, PATH_MAX); + getcwd(cwd, PATH_MAX); #endif - orig_cwd = cwd; + orig_cwd = cwd; #ifndef _WIN32 - /* If we're root, issue a warning now */ - if (!getuid() && !getgid()) - { - fprintf(stderr, "WARNING: You are currently running Anope as the root superuser. Anope does not\n"); - fprintf(stderr, " require root privileges to run, and it is discouraged that you run Anope\n"); - fprintf(stderr, " as the root superuser.\n"); - } + /* If we're root, issue a warning now */ + if (!getuid() && !getgid()) + { + fprintf(stderr, "WARNING: You are currently running Anope as the root superuser. Anope does not\n"); + fprintf(stderr, " require root privileges to run, and it is discouraged that you run Anope\n"); + fprintf(stderr, " as the root superuser.\n"); + } #endif - binary_dir = GetFullProgDir(av[0]); - if (binary_dir[binary_dir.length() - 1] == '.') - binary_dir = binary_dir.substr(0, binary_dir.length() - 2); + binary_dir = GetFullProgDir(av[0]); + if (binary_dir[binary_dir.length() - 1] == '.') + binary_dir = binary_dir.substr(0, binary_dir.length() - 2); #ifdef _WIN32 - Anope::string::size_type n = binary_dir.rfind('\\'); - services_dir = binary_dir.substr(0, n) + "\\data"; + Anope::string::size_type n = binary_dir.rfind('\\'); + services_dir = binary_dir.substr(0, n) + "\\data"; #else - Anope::string::size_type n = binary_dir.rfind('/'); - services_dir = binary_dir.substr(0, n) + "/data"; + Anope::string::size_type n = binary_dir.rfind('/'); + services_dir = binary_dir.substr(0, n) + "/data"; #endif - /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */ - ModuleManager::CleanupRuntimeDirectory(); + /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */ + ModuleManager::CleanupRuntimeDirectory(); + try + { /* General initialization first */ Init(ac, av); + } + catch (const FatalException &ex) + { + Log() << ex.GetReason(); + return -1; + } - /* If the first connect fails give up, don't sit endlessly trying to reconnect */ - if (!Connect()) - { - Log() << "Can't connect to any servers"; - return 0; - } + try + { + Connect(); + } + catch (const SocketException &ex) + { + Log() << ex.GetReason(); + ModuleManager::UnloadAll(); + SocketEngine::Shutdown(); + for (Module *m; (m = ModuleManager::FindFirstOf(PROTOCOL)) != NULL;) + ModuleManager::UnloadModule(m, NULL); + ModuleManager::CleanupRuntimeDirectory(); + return -1; + } - ircdproto->SendConnect(); - FOREACH_MOD(I_OnServerConnect, OnServerConnect()); + started = true; - started = true; + /* Set up timers */ + time_t last_check = Anope::CurTime; + UpdateTimer updateTimer(Config->UpdateTimeout); - /* Set up timers */ - time_t last_check = Anope::CurTime; - UpdateTimer updateTimer(Config->UpdateTimeout); + /*** Main loop. ***/ + while (!quitting) + { + Log(LOG_DEBUG_2) << "Top of main loop"; - /*** Main loop. ***/ - while (!quitting) + if (Anope::CurTime - last_check >= Config->TimeoutCheck) { - while (!quitting && UplinkSock) - { - Log(LOG_DEBUG_2) << "Top of main loop"; - - if (!readonly && (save_data || shutting_down)) - { - if (shutting_down) - ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait."); - save_databases(); - save_data = false; - } - - if (shutting_down) - { - quitting = true; - break; - } + TimerManager::TickTimers(Anope::CurTime); + last_check = Anope::CurTime; + } - if (Anope::CurTime - last_check >= Config->TimeoutCheck) - { - TimerManager::TickTimers(Anope::CurTime); - last_check = Anope::CurTime; - } + /* Free up any finished threads */ + threadEngine.Process(); - /* Free up any finished threads */ - threadEngine.Process(); + /* Process any modes that need to be (un)set */ + if (Me->IsSynced()) + ModeManager::ProcessModes(); - /* Process any modes that need to be (un)set */ - if (Me != NULL && Me->IsSynced()) - ModeManager::ProcessModes(); + /* Process the socket engine */ + SocketEngine::Process(); + } - /* Process the socket engine */ - SocketEngine::Process(); - } + if (save_data) + { + ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait."); + save_databases(); + save_data = false; + } - if (quitting) - /* Disconnect and exit */ - services_shutdown(); - else - { - FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect()); + if (restarting) + { + FOREACH_MOD(I_OnRestart, OnRestart()); + } + else + { + FOREACH_MOD(I_OnShutdown, OnShutdown()); + } - /* Clear all of our users, but not our bots */ - for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) - { - User *u = it->second; - ++it; + if (quitmsg.empty()) + quitmsg = "Terminating, reason unknown"; + Log() << quitmsg; - if (u->server != Me) - delete u; - } + ModuleManager::UnloadAll(); + SocketEngine::Shutdown(); + for (Module *m; (m = ModuleManager::FindFirstOf(PROTOCOL)) != NULL;) + ModuleManager::UnloadModule(m, NULL); - Me->SetFlag(SERVER_SYNCING); - for (unsigned i = Me->GetLinks().size(); i > 0; --i) - if (!Me->GetLinks()[i - 1]->HasFlag(SERVER_JUPED)) - delete Me->GetLinks()[i - 1]; + ModuleManager::CleanupRuntimeDirectory(); - unsigned j = 0; - for (; j < (Config->MaxRetries ? Config->MaxRetries : j + 1); ++j) - { - Log() << "Disconnected from the server, retrying in " << Config->RetryWait << " seconds"; - - sleep(Config->RetryWait); - if (Connect()) - { - ircdproto->SendConnect(); - FOREACH_MOD(I_OnServerConnect, OnServerConnect()); - break; - } - } - if (Config->MaxRetries && j == Config->MaxRetries) - { - Log() << "Max connection retry limit exceeded"; - quitting = true; - } - } - } - } - catch (const FatalException &ex) + if (restarting) { - if (!ex.GetReason().empty()) - Log(LOG_TERMINAL) << ex.GetReason(); - if (started) - services_shutdown(); - return -1; + chdir(binary_dir.c_str()); + av[0] = const_cast<char *>(("./" + services_bin).c_str()); + execve(services_bin.c_str(), av, envp); + Log() << "Restart failed"; + return_code = -1; } return 0; diff --git a/src/messages.cpp b/src/messages.cpp index f8d893eca..ed9e572ce 100644 --- a/src/messages.cpp +++ b/src/messages.cpp @@ -25,7 +25,7 @@ bool OnStats(const Anope::string &source, const std::vector<Anope::string> ¶ if (u && u->HasMode(UMODE_OPER)) { ircdproto->SendNumeric(Config->ServerName, 211, source, "Server SendBuf SentBytes SentMsgs RecvBuf RecvBytes RecvMsgs ConnTime"); - ircdproto->SendNumeric(Config->ServerName, 211, source, "%s %d %d %d %d %d %d %ld", uplink_server->host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - start_time)); + ircdproto->SendNumeric(Config->ServerName, 211, source, "%s %d %d %d %d %d %d %ld", Config->Uplinks[CurrentUplink]->host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - start_time)); } ircdproto->SendNumeric(Config->ServerName, 219, source, "%c :End of /STATS report.", params[0][0]); diff --git a/src/misc.cpp b/src/misc.cpp index fff224eed..966af359e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -843,6 +843,15 @@ void Anope::Unhex(const Anope::string &src, char *dest) dest[destpos] = 0; } +int Anope::LastErrorCode() +{ +#ifndef _WIN32 + return errno; +#else + return GetLastError(); +#endif +} + const Anope::string Anope::LastError() { #ifndef _WIN32 diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp index e2f837f0f..1c4e07eef 100644 --- a/src/modulemanager.cpp +++ b/src/modulemanager.cpp @@ -308,6 +308,8 @@ ModuleReturn ModuleManager::DeleteModule(Module *m) ano_module_t handle = m->handle; Anope::string filename = m->filename; + Log(LOG_DEBUG) << "Unloading module " << m->name; + ano_modclearerr(); void (*destroy_func)(Module *m) = function_cast<void (*)(Module *)>(dlsym(m->handle, "AnopeFini")); const char *err = ano_moderr(); @@ -471,14 +473,14 @@ void ModuleManager::ClearCallBacks(Module *m) delete m->CallBacks.front(); } -/** Unloading all modules, NEVER call this when Anope isn't shutting down. - * Ever. +/** Unloading all modules except the protocol module. */ void ModuleManager::UnloadAll() { std::vector<Anope::string> modules[MT_END]; for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) - modules[(*it)->type].push_back((*it)->name); + if ((*it)->type != PROTOCOL) + modules[(*it)->type].push_back((*it)->name); for (size_t i = MT_BEGIN + 1; i != MT_END; ++i) for (unsigned j = 0; j < modules[i].size(); ++j) diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp index 0ced8e71e..316696f54 100644 --- a/src/socketengines/socketengine_epoll.cpp +++ b/src/socketengines/socketengine_epoll.cpp @@ -30,6 +30,8 @@ void SocketEngine::Init() void SocketEngine::Shutdown() { + Process(); + for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it) delete it->second; Sockets.clear(); diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp index b8eef91b8..3202bbe29 100644 --- a/src/socketengines/socketengine_poll.cpp +++ b/src/socketengines/socketengine_poll.cpp @@ -37,6 +37,8 @@ void SocketEngine::Init() void SocketEngine::Shutdown() { + Process(); + for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it) delete it->second; Sockets.clear(); diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp index 43b9b17cc..e23046b79 100644 --- a/src/socketengines/socketengine_select.cpp +++ b/src/socketengines/socketengine_select.cpp @@ -13,6 +13,8 @@ void SocketEngine::Init() void SocketEngine::Shutdown() { + Process(); + for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it) delete it->second; Sockets.clear(); diff --git a/src/sockets.cpp b/src/sockets.cpp index 96acbe981..c9758cb12 100644 --- a/src/sockets.cpp +++ b/src/sockets.cpp @@ -251,7 +251,7 @@ bool cidr::match(sockaddrs &other) * @param sz How much to read * @return Number of bytes received */ -int SocketIO::Recv(Socket *s, char *buf, size_t sz) const +int SocketIO::Recv(Socket *s, char *buf, size_t sz) { size_t i = recv(s->GetFD(), buf, sz, 0); TotalRead += i; @@ -263,7 +263,7 @@ int SocketIO::Recv(Socket *s, char *buf, size_t sz) const * @param buf What to write * @return Number of bytes written */ -int SocketIO::Send(Socket *s, const Anope::string &buf) const +int SocketIO::Send(Socket *s, const Anope::string &buf) { size_t i = send(s->GetFD(), buf.c_str(), buf.length(), 0); TotalWritten += i; @@ -272,8 +272,9 @@ int SocketIO::Send(Socket *s, const Anope::string &buf) const /** Accept a connection from a socket * @param s The socket + * @return The new client socket */ -void SocketIO::Accept(ListenSocket *s) +ClientSocket *SocketIO::Accept(ListenSocket *s) { sockaddrs conaddr; @@ -285,32 +286,44 @@ void SocketIO::Accept(ListenSocket *s) #endif if (newsock > 0 && newsock != INVALID_SOCKET) - s->OnAccept(newsock, conaddr); + return s->OnAccept(newsock, conaddr); else throw SocketException("Unable to accept connection: " + Anope::LastError()); } +/** Bind a socket + * @param s The socket + * @param ip The IP to bind to + * @param port The optional port to bind to + */ +void SocketIO::Bind(Socket *s, const Anope::string &ip, int port) +{ + s->bindaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, ip, port); + if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1) + throw SocketException("Unable to bind to address: " + Anope::LastError()); +} + /** Connect the socket * @param s THe socket * @param target IP to connect to * @param port to connect to - * @param bindip IP to bind to, if any */ -void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port, const Anope::string &bindip) +void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port) { - s->bindaddr.clear(); - s->conaddr.clear(); - - if (!bindip.empty()) + s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port); + int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size()); + if (c == -1) { - s->bindaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, bindip, 0); - if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + Anope::LastError()); + if (Anope::LastErrorCode() != EINPROGRESS) + throw SocketException("Error connecting to server: " + Anope::LastError()); + else + SocketEngine::MarkWritable(s); + } + else + { + s->OnConnect(); + s->connected = true; } - - s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port); - if (connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size()) == -1 && errno != EINPROGRESS) - throw SocketException(Anope::string("Error connecting to server: ") + Anope::LastError()); } /** Empty constructor, used for things such as the pipe socket @@ -335,6 +348,7 @@ Socket::Socket(int sock, bool ipv6, int type) : Flags<SocketFlag, 2>(SocketFlagS this->Sock = socket(this->IPv6 ? AF_INET6 : AF_INET, type, 0); else this->Sock = sock; + this->SetNonBlocking(); SocketEngine::AddSocket(this); } @@ -391,6 +405,15 @@ bool Socket::SetNonBlocking() #endif } +/** Bind the socket to an ip and port + * @param ip The ip + * @param port The port + */ +void Socket::Bind(const Anope::string &ip, int port) +{ + this->IO->Bind(this, ip, port); +} + /** Called when there is something to be received for this socket * @return true on success, false to drop this socket */ @@ -445,7 +468,9 @@ bool BufferedSocket::ProcessRead() char tbuffer[NET_BUFSIZE] = ""; RecvLen = this->IO->Recv(this, tbuffer, sizeof(tbuffer) - 1); - if (RecvLen <= 0) + if (RecvLen == -2) + return true; + else if (RecvLen <= 0) return false; Anope::string sbuffer = this->extrabuf; @@ -482,10 +507,10 @@ bool BufferedSocket::ProcessRead() */ bool BufferedSocket::ProcessWrite() { - if (this->WriteBuffer.empty()) - return true; int count = this->IO->Send(this, this->WriteBuffer); - if (count <= -1) + if (count == -2) + return true; + else if (count <= -1) return false; this->WriteBuffer = this->WriteBuffer.substr(count); if (this->WriteBuffer.empty()) @@ -557,10 +582,8 @@ ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool ipv6) : S this->Type = SOCKTYPE_LISTEN; this->SetNonBlocking(); - this->listenaddrs.pton(IPv6 ? AF_INET6 : AF_INET, bindip, port); - - if (bind(Sock, &this->listenaddrs.sa, this->listenaddrs.size()) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + Anope::LastError()); + this->bindaddr.pton(IPv6 ? AF_INET6 : AF_INET, bindip, port); + this->IO->Bind(this, bindip, port); if (listen(Sock, 5) == -1) throw SocketException(Anope::string("Unable to listen: ") + Anope::LastError()); @@ -601,7 +624,7 @@ ClientSocket *ListenSocket::OnAccept(int fd, const sockaddrs &addr) * @param ipv6 true to use IPv6 * @param type The socket type, defaults to SOCK_STREAM */ -ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6, type) +ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6, type), connected(false) { this->Type = SOCKTYPE_CONNECTION; } @@ -609,19 +632,58 @@ ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6 /** Connect the socket * @param TargetHost The target host to connect to * @param Port The target port to connect to - * @param BindHost The host to bind to for connecting */ -void ConnectionSocket::Connect(const Anope::string &TargetHost, int Port, const Anope::string &BindHost) +void ConnectionSocket::Connect(const Anope::string &TargetHost, int Port) { - try - { - this->IO->Connect(this, TargetHost, Port, BindHost); - } - catch (const SocketException &) + this->IO->Connect(this, TargetHost, Port); +} + +/** Called when the socket is ready to be written to + * @return true on success, false to drop this socket + */ +bool ConnectionSocket::ProcessWrite() +{ + if (!this->connected) { - delete this; - throw; + SocketEngine::ClearWritable(this); + + int optval = 0; + socklen_t optlen = sizeof(optval); + if (!getsockopt(this->GetFD(), SOL_SOCKET, SO_ERROR, &optval, &optlen) && !optval) + { + this->OnConnect(); + this->connected = true; + return true; + } + + errno = optval; + this->OnError(Anope::LastError()); + return false; } + + return BufferedSocket::ProcessWrite(); +} + +/** Called when there is an error for this socket + * @return true on success, false to drop this socket + */ +void ConnectionSocket::ProcessError() +{ + if (!this->connected) + this->ProcessWrite(); +} + +/** Called on a successful connect + */ +void ConnectionSocket::OnConnect() +{ +} + +/** Called when a connection is not successful + * @param error The error + */ +void ConnectionSocket::OnError(const Anope::string &error) +{ } /** Constructor |