diff options
author | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-04-08 20:23:00 +0000 |
---|---|---|
committer | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-04-08 20:23:00 +0000 |
commit | 973ecb7058325931d14a9537e5e3e57595347a10 (patch) | |
tree | 43585f36085949a1b494374572fa4e3dc25b0960 /src | |
parent | e1ff14e0c44d0a115372a821559b782c57c793f3 (diff) |
Rewrote sockets. This adds support for IPv6 and makes Anope capable of reconnecting if it loses connection to the uplink.
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2862 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/config.c | 39 | ||||
-rw-r--r-- | src/core/cs_xop.c | 19 | ||||
-rw-r--r-- | src/core/db_plain.cpp | 2 | ||||
-rw-r--r-- | src/core/os_shutdown.c | 3 | ||||
-rw-r--r-- | src/core/os_stats.c | 4 | ||||
-rw-r--r-- | src/init.c | 55 | ||||
-rw-r--r-- | src/log.c | 4 | ||||
-rw-r--r-- | src/main.c | 178 | ||||
-rw-r--r-- | src/messages.c | 3 | ||||
-rw-r--r-- | src/process.c | 11 | ||||
-rw-r--r-- | src/protocol/bahamut.c | 2 | ||||
-rw-r--r-- | src/protocol/inspircd11.c | 2 | ||||
-rw-r--r-- | src/protocol/unreal32.c | 5 | ||||
-rw-r--r-- | src/send.c | 8 | ||||
-rw-r--r-- | src/servers.c | 9 | ||||
-rw-r--r-- | src/sockets.cpp | 459 | ||||
-rw-r--r-- | src/sockutil.c | 718 | ||||
-rw-r--r-- | src/windows.cpp | 85 |
20 files changed, 719 insertions, 895 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6343e8c3..4bad367b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,9 +64,9 @@ endif(MSVC) # Generate the Anope executable and set it's linker flags, also set it to export it's symbols even though it's not a module add_executable(${PROGRAM_NAME} ${SRC_SRCS}) set_target_properties(${PROGRAM_NAME} PROPERTIES LINKER_LANGUAGE CXX LINK_FLAGS "${LDFLAGS}" ENABLE_EXPORTS ON) -# On Windows, also link Anope to the wsock32 library, as well as set the version +# On Windows, also link Anope to the wsock32 and Ws2_32 library, as well as set the version if(WIN32) - target_link_libraries(${PROGRAM_NAME} wsock32 ${WIN32_MEMORY}) + target_link_libraries(${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY}) set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") endif(WIN32) # Building the Anope executable requires the language files to be compiled first as well as the version.h header to be generated diff --git a/src/Makefile b/src/Makefile index e5a883d1b..949e7e8ec 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ OBJS = actions.o base64.o bots.o botserv.o channels.o chanserv.o command.o commands.o compat.o \ config.o encrypt.o hashcomp.o hostserv.o init.o ircd.o language.o log.o mail.o main.o \ memory.o memoserv.o messages.o misc.o modules.o nickserv.o operserv.o \ - process.o protocol.o send.o servers.o sessions.o slist.o sockutil.o opertype.o users.o module.o modulemanager.o configreader.o \ + process.o protocol.o send.o servers.o sessions.o slist.o sockets.o opertype.o users.o module.o modulemanager.o configreader.o \ wildcard.o nickcore.o nickalias.o timers.o modes.o regchannel.o INCLUDES = ../include/commands.h ../include/defs.h ../include/language.h \ @@ -71,7 +71,7 @@ send.o: send.c $(INCLUDES) servers.o: servers.c $(INCLUDES) sessions.o: sessions.c $(INCLUDES) slist.o: slist.c $(INCLUDES) -sockutil.o: sockutil.c $(INCLUDES) +sockets.o: sockets.cpp $(INCLUDES) opertype.o: opertype.cpp $(INCLUDES) users.o: users.c $(INCLUDES) vsnprintf.o: vsnprintf.c $(INCLUDES) diff --git a/src/config.c b/src/config.c index 70505c8c6..f023d07c2 100644 --- a/src/config.c +++ b/src/config.c @@ -144,7 +144,7 @@ void ServerConfig::ValidateHostname(const char *p, const std::string &tag, const if (!strcasecmp(p, "localhost")) return; - int num_dots = 0; + int num_dots = 0, num_seps = 0; if (*p) { if (*p == '.') @@ -157,9 +157,13 @@ void ServerConfig::ValidateHostname(const char *p, const std::string &tag, const throw ConfigException(std::string("The value of <") + tag + ":" + val + "> is not a valid hostname"); case '.': ++num_dots; + break; + case ':': + ++num_seps; + break; } } - if (!num_dots) + if (!num_dots && !num_seps) throw ConfigException(std::string("The value of <") + tag + ":" + val + "> is not a valid hostname"); } } @@ -382,8 +386,9 @@ bool DoUplink(ServerConfig *conf, const char *, const char **, ValueList &values if (!bail) return true; // Validation variables - const char *host = values[0].GetString(), *password = values[2].GetString(); - int port = values[1].GetInteger(); + const char *host = values[0].GetString(), *password = values[3].GetString(); + int port = values[2].GetInteger(); + bool ipv6 = values[1].GetBool(); ValueItem vi_host(host), vi_port(port), vi_password(password); // Validate the host to make sure it is not empty if (!ValidateNotEmpty(conf, "uplink", "host", vi_host)) @@ -395,7 +400,7 @@ bool DoUplink(ServerConfig *conf, const char *, const char **, ValueList &values if (!ValidateNotEmpty(conf, "uplink", "password", vi_password)) throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information."); // If we get here, all the values are valid, we'll add it to the Uplinks list - Config.Uplinks.push_back(new Uplink(host, port, password)); + Config.Uplinks.push_back(new Uplink(host, port, password, ipv6)); return true; } @@ -597,7 +602,6 @@ int ServerConfig::Read(bool bail) {"serverinfo", "name", "", new ValueContainerChar(&Config.ServerName), DT_HOSTNAME | DT_NORELOAD, ValidateNotEmpty}, {"serverinfo", "description", "", new ValueContainerChar(&Config.ServerDesc), DT_CHARPTR | DT_NORELOAD, ValidateNotEmpty}, {"serverinfo", "localhost", "", new ValueContainerChar(&Config.LocalHost), DT_HOSTNAME | DT_NORELOAD, NoValidation}, - {"serverinfo", "localport", "0", new ValueContainerUInt(&Config.LocalPort), DT_UINTEGER | DT_NORELOAD, ValidatePort}, {"serverinfo", "type", "", new ValueContainerChar(&Config.IRCDModule), DT_CHARPTR | DT_NORELOAD, ValidateNotEmpty}, {"serverinfo", "id", "", new ValueContainerChar(&Config.Numeric), DT_NOSPACES | DT_NORELOAD, NoValidation}, {"serverinfo", "ident", "", new ValueContainerChar(&Config.ServiceUser), DT_CHARPTR | DT_NORELOAD, ValidateNotEmpty}, @@ -645,6 +649,8 @@ int ServerConfig::Read(bool bail) {"options", "enablelogchannel", "no", new ValueContainerBool(&LogChan), DT_BOOLEAN, NoValidation}, {"options", "mlock", "+nrt", new ValueContainerString(&Config.MLock), DT_STRING, NoValidation}, {"options", "botmodes", "", new ValueContainerString(&Config.BotModes), DT_STRING, NoValidation}, + {"options", "maxretries", "10", new ValueContainerUInt(&Config.MaxRetries), DT_UINTEGER, NoValidation}, + {"options", "retrywait", "60", new ValueContainerInt(&Config.RetryWait), DT_INTEGER, ValidateNotZero}, {"nickserv", "nick", "NickServ", new ValueContainerChar(&Config.s_NickServ), DT_CHARPTR | DT_NORELOAD, ValidateNotEmpty}, {"nickserv", "description", "Nickname Registration Service", new ValueContainerChar(&Config.desc_NickServ), DT_CHARPTR | DT_NORELOAD, ValidateNotEmpty}, {"nickserv", "emailregistration", "no", new ValueContainerBool(&Config.NSEmailReg), DT_BOOLEAN, NoValidation}, @@ -757,9 +763,9 @@ int ServerConfig::Read(bool bail) * which is different to the code for reading the singular tags listed above. */ MultiConfig MultiValues[] = { {"uplink", - {"host", "port", "password", NULL}, - {"", "0", "", NULL}, - {DT_HOSTNAME | DT_NORELOAD, DT_UINTEGER | DT_NORELOAD, DT_NOSPACES | DT_NORELOAD}, + {"host", "ipv6", "port", "password", NULL}, + {"", "no", "0", "", NULL}, + {DT_HOSTNAME | DT_NORELOAD, DT_BOOLEAN | DT_NORELOAD, DT_UINTEGER | DT_NORELOAD, DT_NOSPACES | DT_NORELOAD}, InitUplinks, DoUplink, DoneUplinks}, {"module", {"name", NULL}, @@ -1526,21 +1532,6 @@ int read_config(int reload) retval = Config.Read(reload ? false : true); if (!retval) return 0; // Temporary until most of the below is modified to use the new parser -- CyberBotX - if (!reload) { - if (Config.LocalHost) { - std::list<Uplink *>::iterator curr_uplink = Config.Uplinks.begin(), end_uplink = Config.Uplinks.end(); - for (; curr_uplink != end_uplink; ++curr_uplink) { - Uplink *this_uplink = *curr_uplink; - if (!stricmp(Config.LocalHost, this_uplink->host) && Config.LocalPort == this_uplink->port) { - printf("\n<serverinfo:localhost> matches an <uplink:host> entry (%s)\nand <serverinfo:localport> matches an <uplink:port> entry (%d).\nThis will fail, you must make sure they are different.\n", this_uplink->host, this_uplink->port); - retval = 0; - } - } - } - // Just in case someone put something in for <serverinfo:localport> without defining <serverinfo:localhost> too - else Config.LocalPort = 0; - } - if (temp_nsuserhost) { if (!(s = strchr(temp_nsuserhost, '@'))) { Config.NSEnforcerUser = temp_nsuserhost; diff --git a/src/core/cs_xop.c b/src/core/cs_xop.c index 2fe9daf30..fdedfa9bf 100644 --- a/src/core/cs_xop.c +++ b/src/core/cs_xop.c @@ -547,14 +547,11 @@ class CSXOP : public Module if (serv_uplink && is_sync(serv_uplink)) OnUplinkSync(); - else - { - /* We don't want to add some commands until we are synced, so we know what modes - * exist and what dont - */ - ModuleManager::Attach(I_OnUplinkSync, this); - } - ModuleManager::Attach(I_OnChanServHelp, this); + + Implementation i[] = { + I_OnUplinkSync, I_OnServerDisconnect, I_OnChanServHelp + }; + ModuleManager::Attach(i, this, 3); } void OnUplinkSync() @@ -565,6 +562,12 @@ class CSXOP : public Module this->AddCommand(CHANSERV, new CommandCSHOP()); } + void OnServerDisconnect() + { + this->DelCommand(CHANSERV, "QOP"); + this->DelCommand(CHANSERV, "HOP"); + } + void OnChanServHelp(User *u) { if (ModeManager::FindChannelModeByName(CMODE_OWNER)) diff --git a/src/core/db_plain.cpp b/src/core/db_plain.cpp index 02dc71b3b..fb73d2860 100644 --- a/src/core/db_plain.cpp +++ b/src/core/db_plain.cpp @@ -48,7 +48,7 @@ static void ReadDatabase(Module *m = NULL) if (!db.is_open()) { - ircdproto->SendGlobops(NULL, "Unable to open %s for reading!", DatabaseFile.c_str()); + Alog() << "Unable to open " << DatabaseFile << " for reading!"; return; } diff --git a/src/core/os_shutdown.c b/src/core/os_shutdown.c index 670b23f57..3adc2638f 100644 --- a/src/core/os_shutdown.c +++ b/src/core/os_shutdown.c @@ -33,8 +33,7 @@ class CommandOSShutdown : public Command if (Config.GlobalOnCycle) oper_global(NULL, "%s", Config.GlobalOnCycleMessage); - save_data = 1; - delayed_quit = 1; + shutting_down = 1; return MOD_CONT; } diff --git a/src/core/os_stats.c b/src/core/os_stats.c index 4ff9617ae..f159c4806 100644 --- a/src/core/os_stats.c +++ b/src/core/os_stats.c @@ -225,8 +225,8 @@ class CommandOSStats : public Command { long count, mem; - notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_READ, total_read / 1024); - notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_WRITTEN, total_written / 1024); + notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_READ, TotalRead / 1024); + notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_WRITTEN, TotalWritten / 1024); get_user_stats(&count, &mem); notice_lang(Config.s_OperServ, u, OPER_STATS_USER_MEM, count, (mem + 512) / 1024); diff --git a/src/init.c b/src/init.c index 5a819dcfe..e527f32c5 100644 --- a/src/init.c +++ b/src/init.c @@ -367,14 +367,6 @@ int init_secondary(int ac, char **av) } } #else - /* Initialize winsocks -- codemastr */ - { - WSADATA wsa; - if (WSAStartup(MAKEWORD(1, 1), &wsa)) { - Alog() << "Failed to initialized WinSock library"; - return -1; - } - } if (!SupportedWindowsVersion()) { char *winver = GetWindowsVersion(); @@ -487,56 +479,23 @@ int init_secondary(int ac, char **av) if (!bi) { if (Config.s_OperServ) - bi = new BotInfo(Config.s_OperServ, Config.ServiceUser, Config.ServiceHost, Config.desc_OperServ); + new BotInfo(Config.s_OperServ, Config.ServiceUser, Config.ServiceHost, Config.desc_OperServ); if (Config.s_NickServ) - bi = new BotInfo(Config.s_NickServ, Config.ServiceUser, Config.ServiceHost, Config.desc_NickServ); + new BotInfo(Config.s_NickServ, Config.ServiceUser, Config.ServiceHost, Config.desc_NickServ); if (Config.s_ChanServ) - bi = new BotInfo(Config.s_ChanServ, Config.ServiceUser, Config.ServiceHost, Config.desc_ChanServ); + new BotInfo(Config.s_ChanServ, Config.ServiceUser, Config.ServiceHost, Config.desc_ChanServ); if (Config.s_HostServ) - bi = new BotInfo(Config.s_HostServ, Config.ServiceUser, Config.ServiceHost, Config.desc_HostServ); + new BotInfo(Config.s_HostServ, Config.ServiceUser, Config.ServiceHost, Config.desc_HostServ); if (Config.s_MemoServ) - bi = new BotInfo(Config.s_MemoServ, Config.ServiceUser, Config.ServiceHost, Config.desc_MemoServ); + new BotInfo(Config.s_MemoServ, Config.ServiceUser, Config.ServiceHost, Config.desc_MemoServ); if (Config.s_BotServ) - bi = new BotInfo(Config.s_BotServ, Config.ServiceUser, Config.ServiceHost, Config.desc_BotServ); + new BotInfo(Config.s_BotServ, Config.ServiceUser, Config.ServiceHost, Config.desc_BotServ); if (Config.s_GlobalNoticer) - bi = new BotInfo(Config.s_GlobalNoticer, Config.ServiceUser, Config.ServiceHost, Config.desc_GlobalNoticer); + new BotInfo(Config.s_GlobalNoticer, Config.ServiceUser, Config.ServiceHost, Config.desc_GlobalNoticer); } FOREACH_MOD(I_OnPostLoadDatabases, OnPostLoadDatabases()); - FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect()); - - /* Connect to the remote server */ - std::list<Uplink *>::iterator curr_uplink = Config.Uplinks.begin(), end_uplink = Config.Uplinks.end(); - int servernum = 1; - for (; curr_uplink != end_uplink; ++curr_uplink, ++servernum) { - uplink_server = *curr_uplink; - servsock = conn(uplink_server->host, uplink_server->port, Config.LocalHost, Config.LocalPort); - if (servsock >= 0) { - Alog() << "Connected to Server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << ")"; - break; - } - } - if (curr_uplink == end_uplink) fatal_perror("Can't connect to any servers"); - - ircdproto->SendConnect(); - FOREACH_MOD(I_OnServerConnect, OnServerConnect()); - - sgets2(inbuf, sizeof(inbuf), servsock); - if (strnicmp(inbuf, "ERROR", 5) == 0) { - /* Close server socket first to stop wallops, since the other - * server doesn't want to listen to us anyway */ - disconn(servsock); - servsock = -1; - fatal("Remote server returned: %s", inbuf); - } - - /* Announce a logfile error if there was one */ - if (openlog_failed) { - ircdproto->SendGlobops(NULL, "Warning: couldn't open logfile: %s", - strerror(openlog_errno)); - } - /* Success! */ return 0; } @@ -207,7 +207,7 @@ void fatal(const char *fmt, ...) fprintf(logfile, "%s FATAL: %s\n", buf, buf2); if (nofork) fprintf(stderr, "%s FATAL: %s\n", buf, buf2); - if (servsock >= 0) + if (UplinkSock) ircdproto->SendGlobops(NULL, "FATAL ERROR! %s", buf2); /* one of the many places this needs to be called from */ @@ -245,7 +245,7 @@ void fatal_perror(const char *fmt, ...) if (nofork) fprintf(stderr, "%s FATAL: %s: %s\n", buf, buf2, strerror(errno_save)); - if (servsock >= 0) + if (UplinkSock) ircdproto->SendGlobops(NULL, "FATAL ERROR! %s: %s", buf2, strerror(errno_save)); diff --git a/src/main.c b/src/main.c index 28f1fd51d..7288e832d 100644 --- a/src/main.c +++ b/src/main.c @@ -64,17 +64,11 @@ std::string binary_dir; /* Used to store base path for Anope */ int quitting = 0; /* Set to 1 if we are to quit after saving databases */ -int delayed_quit = 0; +int shutting_down = 0; /* Contains a message as to why services is terminating */ const char *quitmsg = NULL; -/* Input buffer - global, so we can dump it if something goes wrong */ -char inbuf[BUFSIZE]; - -/* Socket for talking to server */ -int servsock = -1; - /* Should we update the databases now? */ int save_data = 0; @@ -123,6 +117,28 @@ class UpdateTimer : public Timer } }; +Socket *UplinkSock = NULL; + +class UplinkSocket : public Socket +{ + public: + UplinkSocket(const std::string &nTargetHost, int nPort, const std::string &nBindHost = "", bool nIPv6 = false) : Socket(nTargetHost, nPort, nBindHost, nIPv6) + { + UplinkSock = this; + } + + ~UplinkSocket() + { + UplinkSock = NULL; + } + + bool Read(const std::string &buf) + { + process(buf); + return true; + } +}; + /*************************************************************************/ /* Run expiration routines */ @@ -160,6 +176,9 @@ extern void expire_all() void save_databases() { + if (readonly) + return; + EventReturn MOD_RESULT; FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase()); Alog(LOG_DEBUG) << "Saving FFF databases"; @@ -181,7 +200,7 @@ void do_restart_services() if (!quitmsg) quitmsg = "Restarting"; ircdproto->SendSquit(Config.ServerName, quitmsg); - disconn(servsock); + delete UplinkSock; close_log(); /* First don't unload protocol module, then do so */ modules_unload_all(false); @@ -223,8 +242,8 @@ static void services_shutdown() u = next; } } + delete UplinkSock; FOREACH_MOD(I_OnShutdown, OnShutdown()); - disconn(servsock); /* First don't unload protocol module, then do so */ modules_unload_all(false); modules_unload_all(true); @@ -366,6 +385,34 @@ std::string GetFullProgDir(char *argv0) /*************************************************************************/ +static bool Connect() +{ + /* Connect to the remote server */ + std::list<Uplink *>::iterator curr_uplink = Config.Uplinks.begin(), end_uplink = Config.Uplinks.end(); + int servernum = 1; + for (; curr_uplink != end_uplink; ++curr_uplink, ++servernum) + { + uplink_server = *curr_uplink; + + try + { + new UplinkSocket(uplink_server->host, uplink_server->port, Config.LocalHost ? Config.LocalHost : "", uplink_server->ipv6); + } + catch (SocketException& ex) + { + Alog() << "Unable to connect to server" << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "), " << ex.GetReason(); + continue; + } + + Alog() << "Connected to Server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << ")"; + return true; + } + + return false; +} + +/*************************************************************************/ + /* Main routine. (What does it look like? :-) ) */ int main(int ac, char **av, char **envp) @@ -423,9 +470,14 @@ int main(int ac, char **av, char **envp) if ((i = init_secondary(ac, av)) != 0) return i; + FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect()); + + /* If the first connect fails give up, don't sit endlessly trying to reconnect */ + if (!Connect()) + fatal_perror("Can't connect to any servers"); - /* We have a line left over from earlier, so process it first. */ - process(); + ircdproto->SendConnect(); + FOREACH_MOD(I_OnServerConnect, OnServerConnect()); started = 1; @@ -456,69 +508,71 @@ int main(int ac, char **av, char **envp) /*** Main loop. ***/ while (!quitting) { - time_t t = time(NULL); + while (!quitting && UplinkSock) + { + time_t t = time(NULL); - Alog(LOG_DEBUG_2) << "Top of main loop"; + Alog(LOG_DEBUG_2) << "Top of main loop"; - if (!readonly && save_data) - { - if (!noexpire) - expire_all(); - if (delayed_quit) - ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait."); - save_databases(); - if (save_data < 0) + if (!readonly && (save_data || shutting_down)) + { + if (!noexpire) + expire_all(); + if (shutting_down) + ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait."); + save_databases(); + save_data = 0; + } + + if (shutting_down) + { + quitting = 1; break; - save_data = 0; - } + } - if (delayed_quit) - break; + if (t - last_check >= Config.TimeoutCheck) + { + TimerManager::TickTimers(t); + last_check = t; + } - if (t - last_check >= Config.TimeoutCheck) + /* Process any modes that need to be (un)set */ + ModeManager::ProcessModes(); + + /* Process the socket engine */ + socketEngine.Process(); + } + + if (quitting) { - TimerManager::TickTimers(t); - last_check = t; + /* Disconnect and exit */ + services_shutdown(); } + else + { + FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect()); - /* Process any modes that need to be (un)set */ - ModeManager::ProcessModes(); - - /* this is a nasty nasty typecast. we need to rewrite the - socket stuff -Certus */ - i = static_cast<int>(reinterpret_cast<long>(sgets2(inbuf, sizeof(inbuf), servsock))); - if ((i > 0) || (i < (-1))) { - process(); - } else if (i == 0) { - int errno_save = errno; - quitmsg = new char[BUFSIZE]; - if (quitmsg) { - // Naughty, but oh well. :) - snprintf(const_cast<char *>(quitmsg), BUFSIZE, - "Read error from server: %s (error num: %d)", - strerror(errno_save), errno_save); - } else { - quitmsg = "Read error from server"; + unsigned j = 0; + for (; j < (Config.MaxRetries ? Config.MaxRetries : j + 1); ++j) + { + Alog() << "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) + { + Alog() << "Max connection retry limit exceeded"; + quitting = 1; } - quitting = 1; - - /* Save the databases */ - if (!readonly) - save_databases(); } } - - /* Check for restart instead of exit */ - if (save_data == -2) - { - do_restart_services(); - return 0; - } - - /* Disconnect and exit */ - services_shutdown(); - return 0; } diff --git a/src/messages.c b/src/messages.c index fe8f46542..31ba669eb 100644 --- a/src/messages.c +++ b/src/messages.c @@ -231,8 +231,7 @@ int m_stats(const char *source, int ac, const char **av) if (u && is_oper(u)) { 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, write_buffer_len(), total_written, -1, read_buffer_len(), - total_read, -1, time(NULL) - start_time); + ircdproto->SendNumeric(Config.ServerName, 211, source, "%s %d %d %d %d %d %d %ld", uplink_server->host, UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, time(NULL) - start_time); } ircdproto->SendNumeric(Config.ServerName, 219, source, "%c :End of /STATS report.", *av[0] ? *av[0] : '*'); diff --git a/src/process.c b/src/process.c index 2c85e1644..b593b0d88 100644 --- a/src/process.c +++ b/src/process.c @@ -281,7 +281,7 @@ int split_buf(char *buf, const char ***argv, int colon_special) /* process: Main processing routine. Takes the string in inbuf (global * variable) and does something appropriate with it. */ -void process() +void process(const std::string &buffer) { int retVal = 0; Message *current = NULL; @@ -299,11 +299,11 @@ void process() *cmd = '\0'; /* If debugging, log the buffer */ - Alog(LOG_DEBUG) << "Received: " << inbuf; + Alog(LOG_DEBUG) << "Received: " << buffer; /* First make a copy of the buffer so we have the original in case we * crash - in that case, we want to know what we crashed on. */ - strscpy(buf, inbuf, sizeof(buf)); + strscpy(buf, buffer.c_str(), sizeof(buf)); doCleanBuffer(buf); @@ -359,9 +359,8 @@ void process() } } } - } else { - Alog(LOG_DEBUG) << "unknown message from server (" << inbuf << ")"; - } + } else + Alog(LOG_DEBUG) << "unknown message from server (" << buffer << ")"; /* Free argument list we created */ free(av); diff --git a/src/protocol/bahamut.c b/src/protocol/bahamut.c index 34886126c..6bab9ff2c 100644 --- a/src/protocol/bahamut.c +++ b/src/protocol/bahamut.c @@ -283,7 +283,7 @@ class BahamutIRCdProto : public IRCDProto { bahamut_cmd_pass(uplink_server->password); bahamut_cmd_capab(); - me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, NULL); + me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, ""); SendServer(me_server); bahamut_cmd_svinfo(); bahamut_cmd_burst(); diff --git a/src/protocol/inspircd11.c b/src/protocol/inspircd11.c index 1357f1e46..f193d685c 100644 --- a/src/protocol/inspircd11.c +++ b/src/protocol/inspircd11.c @@ -251,7 +251,7 @@ class InspIRCdProto : public IRCDProto void SendConnect() { inspircd_cmd_pass(uplink_server->password); - me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, NULL); + me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, ""); SendServer(me_server); send_cmd(NULL, "BURST"); send_cmd(Config.ServerName, "VERSION :Anope-%s %s :%s - %s (%s) -- %s", version_number, Config.ServerName, ircd->name, version_flags, Config.EncModuleList.begin()->c_str(), version_build); diff --git a/src/protocol/unreal32.c b/src/protocol/unreal32.c index 1ecd12860..c44293ed8 100644 --- a/src/protocol/unreal32.c +++ b/src/protocol/unreal32.c @@ -291,10 +291,7 @@ class UnrealIRCdProto : public IRCDProto { unreal_cmd_capab(); unreal_cmd_pass(uplink_server->password); - if (Config.Numeric) - me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, Config.Numeric); - else - me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, NULL); + me_server = new_server(NULL, Config.ServerName, Config.ServerDesc, SERVER_ISME, (Config.Numeric ? Config.Numeric : "")); SendServer(me_server); } diff --git a/src/send.c b/src/send.c index a032b1f03..360d981f3 100644 --- a/src/send.c +++ b/src/send.c @@ -35,12 +35,12 @@ void send_cmd(const char *source, const char *fmt, ...) if (source) { - sockprintf(servsock, ":%s %s\r\n", source, buf); + UplinkSock->Write(":%s %s", source, buf); Alog(LOG_DEBUG) << "Sent: :" << source << " " << buf; } else { - sockprintf(servsock, "%s\r\n", buf); + UplinkSock->Write("%s", buf); Alog(LOG_DEBUG) << "Sent: "<< buf; } @@ -62,12 +62,12 @@ void send_cmd(const std::string &source, const char *fmt, ...) if (!source.empty()) { - sockprintf(servsock, ":%s %s\r\n", source.c_str(), buf); + UplinkSock->Write(":%s %s", source.c_str(), buf); Alog(LOG_DEBUG) << "Sent: :" << source << " " << buf; } else { - sockprintf(servsock, "%s\r\n", buf); + UplinkSock->Write("%s", buf); Alog(LOG_DEBUG) << "Sent: " << buf; } diff --git a/src/servers.c b/src/servers.c index c16305c1d..b9ea91171 100644 --- a/src/servers.c +++ b/src/servers.c @@ -118,7 +118,7 @@ Server *next_server(ServerFlag flag) * @return Server Struct */ Server *new_server(Server * server_uplink, const char *name, const char *desc, - ServerFlag flag, const char *suid) + ServerFlag flag, const std::string &suid) { Server *serv; @@ -131,11 +131,10 @@ Server *new_server(Server * server_uplink, const char *name, const char *desc, if (flag != SERVER_START) serv->SetFlag(flag); serv->uplink = server_uplink; - if (suid) { - serv->suid = sstrdup(suid); - } else { + if (!suid.empty()) + serv->suid = sstrdup(suid.c_str()); + else serv->suid = NULL; - } serv->sync = SSYNC_IN_PROGRESS; serv->links = NULL; diff --git a/src/sockets.cpp b/src/sockets.cpp new file mode 100644 index 000000000..58370b4f8 --- /dev/null +++ b/src/sockets.cpp @@ -0,0 +1,459 @@ +#include "services.h" + +SocketEngine socketEngine; +int32 TotalRead = 0; +int32 TotalWritten = 0; + +/** Trims all the \r and \ns from the begining and end of a string + * @return A string without trailing \r and \ns + */ +static void TrimBuf(std::string &buffer) +{ + while (!buffer.empty() && (buffer[0] == '\r' || buffer[0] == '\n')) + buffer.erase(buffer.begin()); + while (!buffer.empty() && (buffer[buffer.length() - 1] == '\r' || buffer[buffer.length() - 1] == '\n')) + buffer.erase(buffer.length() - 1); +} + +/** Default constructor + * @param nTargetHost Hostname to connect to + * @param nPort Port to connect to + * @param nBindHos Host to bind to when connecting + * @param nIPv6 true to use IPv6 + */ +Socket::Socket(const std::string &nTargetHost, int nPort, const std::string &nBindHost, bool nIPv6) : TargetHost(nTargetHost), Port(nPort), BindHost(nBindHost), IPv6(nIPv6) +{ + if (!IPv6 && (TargetHost.find(':') != std::string::npos || BindHost.find(':') != std::string::npos)) + IPv6 = true; + + Sock = socket(IPv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + + addrinfo hints; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = IPv6 ? AF_INET6 : AF_INET; + + if (!BindHost.empty()) + { + addrinfo *bindar; + sockaddr_in bindaddr; + sockaddr_in6 bindaddr6; + + int Bound = -1; + if (getaddrinfo(BindHost.c_str(), NULL, &hints, &bindar) == 0) + { + if (IPv6) + memcpy(&bindaddr6, bindar->ai_addr, bindar->ai_addrlen); + else + memcpy(&bindaddr, bindar->ai_addr, bindar->ai_addrlen); + + freeaddrinfo(bindar); + + Bound = bind(Sock, reinterpret_cast<sockaddr *>(&bindaddr), sizeof(bindaddr)); + } + if (!Bound) + { + if (IPv6) + { + bindaddr6.sin6_family = AF_INET6; + + if (inet_pton(AF_INET6, BindHost.c_str(), &bindaddr6.sin6_addr) < 1) + { + throw SocketException("Invalid bind host"); + } + + if (bind(Sock, reinterpret_cast<sockaddr *>(&bindaddr6), sizeof(bindaddr6)) == -1) + { + throw SocketException("Unable to bind to address"); + } + } + else + { + bindaddr.sin_family = AF_INET; + + if (inet_pton(bindaddr.sin_family, BindHost.c_str(), &bindaddr.sin_addr) < 1) + { + throw SocketException("Invalid bind host"); + } + + if (bind(Sock, reinterpret_cast<sockaddr *>(&bindaddr), sizeof(bindaddr)) == -1) + { + throw SocketException("Unable to bind to address"); + } + } + } + } + + addrinfo *conar; + sockaddr_in conaddr; + sockaddr_in6 conaddr6; + if (getaddrinfo(TargetHost.c_str(), NULL, &hints, &conar) == 0) + { + if (IPv6) + memcpy(&conaddr6, conar->ai_addr, conar->ai_addrlen); + else + memcpy(&conaddr, conar->ai_addr, conar->ai_addrlen); + + freeaddrinfo(conar); + } + else + { + if (IPv6) + { + if (inet_pton(AF_INET6, TargetHost.c_str(), &conaddr6.sin6_addr) < 1) + { + throw SocketException("Invalid server address"); + } + } + else + { + if (inet_pton(AF_INET, TargetHost.c_str(), &conaddr.sin_addr) < 1) + { + throw SocketException("Invalid server address"); + } + } + } + + if (IPv6) + { + conaddr6.sin6_family = AF_INET6; + conaddr6.sin6_port = htons(Port); + + if (connect(Sock, reinterpret_cast<sockaddr *>(&conaddr6), sizeof(conaddr6)) < 0) + { + throw SocketException("Error connecting to server"); + } + } + else + { + conaddr.sin_family = AF_INET; + conaddr.sin_port = htons(Port); + + if (connect(Sock, reinterpret_cast<sockaddr *>(&conaddr), sizeof(conaddr)) < 0) + { + throw SocketException("Error connecting to server"); + } + } + + socketEngine.AddSocket(this); +} + +/** Default destructor + */ +Socket::~Socket() +{ + CloseSocket(Sock); + + socketEngine.DelSocket(this); +} + +/** Read from the socket + * @param buf Buffer to read to + * @param sz How much to read + * @return Number of bytes recieved + */ +int Socket::RecvInternal(char *buf, size_t sz) const +{ + return recv(GetSock(), buf, sz, 0); +} + +/** Write to the socket + * @param buf What to write + * @return Number of bytes sent, -1 on error + */ +int Socket::SendInternal(const std::string &buf) const +{ + return send(GetSock(), buf.c_str(), buf.length(), 0); +} + +/** Get the socket FD for this socket + * @return The fd + */ +int Socket::GetSock() const +{ + return Sock; +} + +/** Check if this socket is IPv6 + * @return true or false + */ +bool Socket::IsIPv6() const +{ + return IPv6; +} + +/** Called when there is something to be read from thie socket + * @return true on success, false to kill this socket + */ +bool Socket::ProcessRead() +{ + char buffer[NET_BUFSIZE]; + memset(&buffer, 0, sizeof(buffer)); + + RecvLen = RecvInternal(buffer, sizeof(buffer)); + if (RecvLen <= 0) + { + return false; + } + TotalRead += RecvLen; + + std::string sbuffer = extrabuf; + sbuffer.append(buffer); + extrabuf.clear(); + size_t lastnewline = sbuffer.find_last_of('\n'); + if (lastnewline < sbuffer.size() - 1) + { + extrabuf = sbuffer.substr(lastnewline); + TrimBuf(extrabuf); + sbuffer = sbuffer.substr(0, lastnewline); + } + + sepstream stream(sbuffer, '\n'); + std::string buf; + + while (stream.GetToken(buf)) + { + TrimBuf(buf); + + if (!buf.empty()) + { + if (!Read(buf)) + { + return false; + } + } + } + + return true; +} + +/** Called when this socket becomes writeable + * @return true on success, false to drop this socket + */ +bool Socket::ProcessWrite() +{ + size_t Written = SendInternal(WriteBuffer); + if (Written == -1) + { + return false; + } + TotalWritten += Written; + + WriteBuffer.clear(); + return true; +} + +/** Called when there is an error on this socket + */ +void Socket::ProcessError() +{ +} + +/** Called with a message recieved from the socket + * @param buf The message + * @return true on success, false to kill this socket + */ +bool Socket::Read(const std::string &buf) +{ + return true; +} + +/** Write to the socket + * @param message The message to write + */ +void Socket::Write(const char *message, ...) +{ + char buf[BUFSIZE]; + va_list vi; + va_start(vi, message); + vsnprintf(buf, sizeof(buf), message, vi); + va_end(vi); + + std::string sbuf = buf; + Write(sbuf); +} + +/** Write to the socket + * @param message The message to write + */ +void Socket::Write(std::string &message) +{ + WriteBuffer.append(message + "\r\n"); + socketEngine.MarkWriteable(this); +} + +/** Get the length of the read buffer + * @return The length of the read buffer + */ +size_t Socket::ReadBufferLen() const +{ + return RecvLen; +} + +/** Get the length of the write buffer + * @return The length of the write buffer + */ +size_t Socket::WriteBufferLen() const +{ + return WriteBuffer.size(); +} + +/** Constructor + */ +SocketEngine::SocketEngine() +{ + FD_ZERO(&ReadFDs); + FD_ZERO(&WriteFDs); + MaxFD = 0; + +#ifdef _WIN32 + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 0), &wsa)) + { + Alog() << "Failed to initialize WinSock library"; + } +#endif +} + +/** Destructor + */ +SocketEngine::~SocketEngine() +{ +#ifdef _WIN32 + WSACleanup(); +#endif +} + +/** Add a socket to the socket engine + * @param s The socket + */ +void SocketEngine::AddSocket(Socket *s) +{ + if (s->GetSock() > MaxFD) + MaxFD = s->GetSock(); + FD_SET(s->GetSock(), &ReadFDs); + Sockets.insert(s); +} + +/** Delete a socket from the socket engine + * @param s The socket + */ +void SocketEngine::DelSocket(Socket *s) +{ + if (s->GetSock() == MaxFD) + --MaxFD; + FD_CLR(s->GetSock(), &ReadFDs); + FD_CLR(s->GetSock(), &WriteFDs); + Sockets.erase(s); +} + +/** Mark a socket as wanting to be written to + * @param s The socket + */ +void SocketEngine::MarkWriteable(Socket *s) +{ + FD_SET(s->GetSock(), &WriteFDs); +} + +/** Unmark a socket as writeable + * @param s The socket + */ +void SocketEngine::ClearWriteable(Socket *s) +{ + FD_CLR(s->GetSock(), &WriteFDs); +} + +/** Called to iterate through each socket and check for activity + */ +void SocketEngine::Process() +{ + fd_set rfdset = ReadFDs, wfdset = WriteFDs, efdset = ReadFDs; + timeval tval; + + tval.tv_sec = 10; + tval.tv_usec = 0; + + int sresult = select(MaxFD + 1, &rfdset, &wfdset, &efdset, &tval); + + if (sresult == -1) + { + Alog() << "SocketEngine::Process error, " << GetError(); + } + else if (sresult) + { + for (std::set<Socket *>::iterator it = Sockets.begin(); it != Sockets.end(); ++it) + { + Socket *s = *it; + + if (FD_ISSET(s->GetSock(), &efdset)) + { + s->ProcessError(); + OldSockets.insert(s); + continue; + } + if (FD_ISSET(s->GetSock(), &rfdset)) + { + if (!s->ProcessRead()) + OldSockets.insert(s); + } + if (FD_ISSET(s->GetSock(), &wfdset)) + { + ClearWriteable(s); + if (!s->ProcessWrite()) + OldSockets.insert(s); + } + } + } + + while (!OldSockets.empty()) + { + delete (*OldSockets.begin()); + OldSockets.erase(OldSockets.begin()); + } +} + +/** Get the last socket error + * @return The error + */ +const std::string SocketEngine::GetError() const +{ +#ifdef _WIN32 + errno = WSAGetLastError(); +#endif + switch (errno) + { + case EBADF: + return "Socket error, invalid file descriptor given to select()"; + break; + case EINTR: + return "Socket engine caught signal"; + break; +#ifdef WIN32 + case WSANOTINITIALISED: + return "A successful WSAStartup call must occur before using this function."; + break; + case WSAEFAULT: + return "The Windows Sockets implementation was unable to allocate needed resources for its internal operations, or the readfds, writefds, exceptfds, or timeval parameters are not part of the user address space."; + break; + case WSAENETDOWN: + return "The network subsystem has failed."; + break; + case WSAEINVAL: + return "The time-out value is not valid, or all three descriptor parameters were null."; + break; + case WSAEINTR: + return "A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall."; + break; + case WSAEINPROGRESS: + return "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."; + break; + case WSAENOTSOCK: + return "One of the descriptor sets contains an entry that is not a socket."; + break; +#endif + default: + return "Socket engine caught unknown error"; + } +} + diff --git a/src/sockutil.c b/src/sockutil.c deleted file mode 100644 index b5125d142..000000000 --- a/src/sockutil.c +++ /dev/null @@ -1,718 +0,0 @@ -/* Socket utility routines. - * - * (C) 2003-2010 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - * $Id$ - * - */ - -#include "services.h" - -/*************************************************************************/ - -static char read_netbuf[NET_BUFSIZE]; -static char *read_curpos = read_netbuf; /* Next byte to return */ -static char *read_bufend = read_netbuf; /* Next position for data from socket */ -static char *const read_buftop = read_netbuf + NET_BUFSIZE; -int32 total_read = 0; -static char write_netbuf[NET_BUFSIZE]; -static char *write_curpos = write_netbuf; /* Next byte to write to socket */ -static char *write_bufend = write_netbuf; /* Next position for data to socket */ -static char *const write_buftop = write_netbuf + NET_BUFSIZE; -static int write_fd = -1; -int32 total_written; -static int lastchar = EOF; - -/*************************************************************************/ - -/** - * Return amount of data in read buffer. - * @return int32 - */ -int32 read_buffer_len() -{ - if (read_bufend >= read_curpos) { - return read_bufend - read_curpos; - } else { - return (read_bufend + NET_BUFSIZE) - read_curpos; - } -} - -/*************************************************************************/ - -/** - * Read data. - * @param fd File Pointer - * @param buf Buffer - * @param int Length of buffer - * @return int - */ -static int buffered_read(ano_socket_t fd, char *buf, int len) -{ - int nread, left = len; - fd_set fds; - struct timeval tv = { 0, 0 }; - int errno_save = ano_sockgeterr(); - - if (fd < 0) { - ano_sockseterr(SOCKERR_EBADF); - return -1; - } - while (left > 0) { - struct timeval *tvptr = (read_bufend == read_curpos ? NULL : &tv); - FD_ZERO(&fds); - FD_SET(fd, &fds); - while (read_bufend != read_curpos - 1 - && !(read_curpos == read_netbuf - && read_bufend == read_buftop - 1) - && select(fd + 1, &fds, 0, 0, tvptr) == 1) { - int maxread; - tvptr = &tv; /* don't wait next time */ - if (read_bufend < read_curpos) /* wrapped around? */ - maxread = (read_curpos - 1) - read_bufend; - else if (read_curpos == read_netbuf) - maxread = read_buftop - read_bufend - 1; - else - maxread = read_buftop - read_bufend; - nread = ano_sockread(fd, read_bufend, maxread); - errno_save = ano_sockgeterr(); - Alog(LOG_DEBUG_3) << "buffered_read wanted " << maxread << " got " << nread; - if (nread <= 0) - break; - read_bufend += nread; - if (read_bufend == read_buftop) - read_bufend = read_netbuf; - } - if (read_curpos == read_bufend) /* No more data on socket */ - break; - /* See if we can gobble up the rest of the buffer. */ - if (read_curpos + left >= read_buftop && read_bufend < read_curpos) { - nread = read_buftop - read_curpos; - memcpy(buf, read_curpos, nread); - buf += nread; - left -= nread; - read_curpos = read_netbuf; - } - /* Now everything we need is in a single chunk at read_curpos. */ - if (read_bufend > read_curpos && read_bufend - read_curpos < left) - nread = read_bufend - read_curpos; - else - nread = left; - if (nread) { - memcpy(buf, read_curpos, nread); - buf += nread; - left -= nread; - read_curpos += nread; - } - } - total_read += len - left; - Alog(LOG_DEBUG_4) << "buffered_read(" << fd << "," << static_cast<void *>(buf) << ", " << len << ") returning " << len - left; - ano_sockseterr(errno_save); - return len - left; -} - -/*************************************************************************/ - -/** - * Optimized version of the above for reading a single character; returns - * the character in an int or EOF, like fgetc(). - * @param fd File Pointer - * @return int - */ -static int buffered_read_one(ano_socket_t fd) -{ - int nread; - fd_set fds; - struct timeval tv = { 0, 0 }; - char c; - struct timeval *tvptr = (read_bufend == read_curpos ? NULL : &tv); - int errno_save = ano_sockgeterr(); - - if (fd < 0) { - ano_sockseterr(SOCKERR_EBADF); - return -1; - } - FD_ZERO(&fds); - FD_SET(fd, &fds); - while (read_bufend != read_curpos - 1 - && !(read_curpos == read_netbuf - && read_bufend == read_buftop - 1) - && select(fd + 1, &fds, 0, 0, tvptr) == 1) { - int maxread; - tvptr = &tv; /* don't wait next time */ - if (read_bufend < read_curpos) /* wrapped around? */ - maxread = (read_curpos - 1) - read_bufend; - else if (read_curpos == read_netbuf) - maxread = read_buftop - read_bufend - 1; - else - maxread = read_buftop - read_bufend; - nread = ano_sockread(fd, read_bufend, maxread); - errno_save = ano_sockgeterr(); - Alog(LOG_DEBUG_3) << "buffered_read_one wanted " << maxread << ", got " << nread; - if (nread <= 0) - break; - read_bufend += nread; - if (read_bufend == read_buftop) - read_bufend = read_netbuf; - } - if (read_curpos == read_bufend) { /* No more data on socket */ - Alog(LOG_DEBUG_4) << "buffered_read_one(" << fd << ") returning " << EOF; - ano_sockseterr(errno_save); - return EOF; - } - c = *read_curpos++; - if (read_curpos == read_buftop) - read_curpos = read_netbuf; - total_read++; - Alog(LOG_DEBUG_4) << "buffered_read_one(" << fd << ") returning " << c; - return static_cast<int>(c) & 0xFF; -} - -/*************************************************************************/ - -/** - * Return amount of data in write buffer. - * @return int - */ -int32 write_buffer_len() -{ - if (write_bufend >= write_curpos) { - return write_bufend - write_curpos; - } else { - return (write_bufend + NET_BUFSIZE) - write_curpos; - } -} - -/*************************************************************************/ - -/** - * Helper routine to try and write up to one chunk of data from the buffer - * to the socket. Return how much was written. - * @param wait Wait - * @return int - */ -static int flush_write_buffer(int wait) -{ - fd_set fds; - struct timeval tv = { 0, 0 }; - int errno_save = ano_sockgeterr(); - - if (write_bufend == write_curpos || write_fd == -1) - return 0; - FD_ZERO(&fds); - FD_SET(write_fd, &fds); - if (select(write_fd + 1, 0, &fds, 0, wait ? NULL : &tv) == 1) { - int maxwrite, nwritten; - if (write_curpos > write_bufend) /* wrapped around? */ - maxwrite = write_buftop - write_curpos; - else if (write_bufend == write_netbuf) - maxwrite = write_buftop - write_curpos - 1; - else - maxwrite = write_bufend - write_curpos; - nwritten = ano_sockwrite(write_fd, write_curpos, maxwrite); - errno_save = ano_sockgeterr(); - Alog(LOG_DEBUG_3) << "flush_write_buffer wanted " << maxwrite << ", got " << nwritten; - if (nwritten > 0) { - write_curpos += nwritten; - if (write_curpos == write_buftop) - write_curpos = write_netbuf; - total_written += nwritten; - return nwritten; - } - } - ano_sockseterr(errno_save); - return 0; -} - -/*************************************************************************/ - -/** - * Write data. - * @param fd File Pointer - * @param buf Buffer to write - * @param len Length to write - * @return int - */ -static int buffered_write(ano_socket_t fd, char *buf, int len) -{ - int nwritten, left = len; - int errno_save = ano_sockgeterr(); - - if (fd < 0) { - errno = EBADF; - return -1; - } - write_fd = fd; - - while (left > 0) { - - /* Don't try putting anything in the buffer if it's full. */ - if (write_curpos != write_bufend + 1 && - (write_curpos != write_netbuf - || write_bufend != write_buftop - 1)) { - /* See if we need to write up to the end of the buffer. */ - if (write_bufend + left >= write_buftop - && write_curpos <= write_bufend) { - nwritten = write_buftop - write_bufend; - memcpy(write_bufend, buf, nwritten); - buf += nwritten; - left -= nwritten; - write_bufend = write_netbuf; - } - /* Now we can copy a single chunk to write_bufend. */ - if (write_curpos > write_bufend - && write_curpos - write_bufend - 1 < left) - nwritten = write_curpos - write_bufend - 1; - else - nwritten = left; - if (nwritten) { - memcpy(write_bufend, buf, nwritten); - buf += nwritten; - left -= nwritten; - write_bufend += nwritten; - } - } - - /* Now write to the socket as much as we can. */ - if (write_curpos == write_bufend + 1 || - (write_curpos == write_netbuf - && write_bufend == write_buftop - 1)) - flush_write_buffer(1); - else - flush_write_buffer(0); - errno_save = errno; - if (write_curpos == write_bufend + 1 || - (write_curpos == write_netbuf - && write_bufend == write_buftop - 1)) { - /* Write failed on full buffer */ - break; - } - } - Alog(LOG_DEBUG_4) << "buffered_write(" << fd << "," << static_cast<void*>(buf) << "," << len - << ") returning " << len - left; - ano_sockseterr(errno_save); - return len - left; -} - - -/*************************************************************************/ - -/** - * Optimized version of the above for writing a single character; returns - * the character in an int or EOF, like fputc(). Commented out because it - * isn't currently used. - * @param int to write - * @param fd Pointer - * @return int - */ -#if 0 -static int buffered_write_one(int c, ano_socket_t fd) -{ - struct timeval tv = { 0, 0 }; - - if (fd < 0) { - ano_sockseterr(SOCKERR_EBADF); - return -1; - } - write_fd = fd; - - /* Try to flush the buffer if it's full. */ - if (write_curpos == write_bufend + 1 || (write_curpos == write_netbuf && write_bufend == write_buftop - 1)) - { - flush_write_buffer(1); - if (write_curpos == write_bufend + 1 || (write_curpos == write_netbuf && write_bufend == write_buftop - 1)) - { - /* Write failed */ - Alog(LOG_DEBUG_4) << "buffered_write_one(" << fd << ") returning " << EOF; - return EOF; - } - } - - /* Write the character. */ - *write_bufend++ = c; - if (write_bufend == write_buftop) - write_bufend = write_netbuf; - - /* Move it to the socket if we can. */ - flush_write_buffer(0); - - Alog(LOG_DEBUG_4) << "buffered_write_one(" << fd << ") returning " << c; - return (int) c & 0xFF; -} -#endif /* 0 */ - -/*************************************************************************/ - -/** - * sgetc ? - * @param int to read - * @return int - */ -int sgetc(ano_socket_t s) -{ - int c; - - if (lastchar != EOF) { - c = lastchar; - lastchar = EOF; - return c; - } - return buffered_read_one(s); -} - -/*************************************************************************/ - -/** - * sungetc ? - * @param int c - * @param int s - * @return int - */ -int sungetc(int c, int s) -{ - return lastchar = c; -} - -/*************************************************************************/ - -/** - * If connection was broken, return NULL. If the read timed out, return - * (char *)-1. - * @param buf Buffer to get - * @param len Length - * @param s Socket - * @return buffer - */ -char *sgets(char *buf, int len, ano_socket_t s) -{ - int c = 0; - struct timeval tv; - fd_set fds; - char *ptr = buf; - - flush_write_buffer(0); - - if (len == 0) - return NULL; - FD_ZERO(&fds); - FD_SET(s, &fds); - tv.tv_sec = Config.ReadTimeout; - tv.tv_usec = 0; - while (read_buffer_len() == 0 && - (c = select(s + 1, &fds, NULL, NULL, &tv)) < 0) { - if (ano_sockgeterr() != EINTR) - break; - flush_write_buffer(0); - } - if (read_buffer_len() == 0 && c == 0) - return reinterpret_cast<char *>(-1); - c = sgetc(s); - while (--len && (*ptr++ = static_cast<char>(c)) != '\n' && (c = sgetc(s)) >= 0); - if (c < 0) - return NULL; - *ptr = 0; - return buf; -} - -/*************************************************************************/ - -/** - * sgets2: Read a line of text from a socket, and strip newline and - * carriage return characters from the end of the line. - * @param buf Buffer to get - * @param len Length - * @param s Socket - * @return buffer - */ -char *sgets2(char *buf, int len, ano_socket_t s) -{ - char *str = sgets(buf, len, s); - - if (!str || str == reinterpret_cast<char *>(-1)) - return str; - str = buf + strlen(buf) - 1; - if (*str == '\n') - *str-- = 0; - if (*str == '\r') - *str = 0; - return buf; -} - -/*************************************************************************/ - -/** - * Read from a socket. (Use this instead of read() because it has - * buffering.) - * @param s Socket - * @param buf Buffer to get - * @param len Length - * @return int - */ -int sread(ano_socket_t s, char *buf, int len) -{ - return buffered_read(s, buf, len); -} - -/*************************************************************************/ - -/** - * sputs : write buffer - * @param s Socket - * @param str Buffer to write - * @return int - */ -int sputs(char *str, ano_socket_t s) -{ - return buffered_write(s, str, strlen(str)); -} - -/*************************************************************************/ - -/** - * sockprintf : a socket writting printf() - * @param s Socket - * @param fmt format of message - * @param ... various args - * @return int - */ -int sockprintf(ano_socket_t s, const char *fmt, ...) -{ - va_list args; - char buf[16384]; /* Really huge, to try and avoid truncation */ - int value; - - va_start(args, fmt); - value = buffered_write(s, buf, vsnprintf(buf, sizeof(buf), fmt, args)); - va_end(args); - return value; -} - -/*************************************************************************/ - -#if !HAVE_GETHOSTBYNAME - -/** - * Translate an IP dotted-quad address to a 4-byte character string. - * Return NULL if the given string is not in dotted-quad format. - * @param ipaddr IP Address - * @return char 4byte ip char string - */ -static char *pack_ip(const char *ipaddr) -{ - static char ipbuf[4]; - int tmp[4], i; - - if (sscanf(ipaddr, "%d.%d.%d.%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3]) - != 4) - return NULL; - for (i = 0; i < 4; i++) { - if (tmp[i] < 0 || tmp[i] > 255) - return NULL; - ipbuf[i] = static_cast<char>(tmp[i]); - } - return ipbuf; -} - -#endif - -/*************************************************************************/ - -/** - * lhost/lport specify the local side of the connection. If they are not - * given (lhost==NULL, lport==0), then they are left free to vary. - * @param host Remote Host - * @param port Remote Port - * @param lhost Config.LocalHost - * @param lport Config.LocalPort - * @return int if successful - */ -int conn(const char *host, int port, const char *lhost, int lport) -{ -#if HAVE_GETHOSTBYNAME - struct hostent *hp; -#else - char *addr; -#endif - struct sockaddr_in sa, lsa; - ano_socket_t sock; - int sockopt = 1; - - memset(&lsa, 0, sizeof(lsa)); - if (lhost) { -#if HAVE_GETHOSTBYNAME - if ((hp = gethostbyname(lhost)) != NULL) { - memcpy(&lsa.sin_addr, hp->h_addr, hp->h_length); - lsa.sin_family = hp->h_addrtype; -#else - if (addr = pack_ip(lhost)) { - memcpy(&lsa.sin_addr, addr, 4); - lsa.sin_family = AF_INET; -#endif - } else { - lhost = NULL; - } - } - if (lport) - lsa.sin_port = htons(static_cast<unsigned short>(lport)); - - memset(&sa, 0, sizeof(sa)); -#if HAVE_GETHOSTBYNAME - if (!(hp = gethostbyname(host))) - return -1; - memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); - sa.sin_family = hp->h_addrtype; -#else - if (!(addr = pack_ip(host))) - { - Alog() << "conn(): `'" << host << "' is not a valid IP address"; - ano_sockseterr(SOCKERR_EINVAL); - return -1; - } - memcpy(&sa.sin_addr, addr, 4); - sa.sin_family = AF_INET; -#endif - sa.sin_port = htons(static_cast<unsigned short>(port)); - - if ((sock = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) - return -1; - - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&sockopt), sizeof(int)) < 0) - Alog() << "couldn't set SO_REUSEADDR on socket"; - - if ((lhost || lport) && bind(sock, reinterpret_cast<struct sockaddr *>(&lsa), sizeof(lsa)) < 0) - { - int errno_save = ano_sockgeterr(); - ano_sockclose(sock); - ano_sockseterr(errno_save); - return -1; - } - - if (connect(sock, reinterpret_cast<struct sockaddr *>(&sa), sizeof(sa)) < 0) { - int errno_save = ano_sockgeterr(); - ano_sockclose(sock); - ano_sockseterr(errno_save); - return -1; - } - - return sock; -} - -/*************************************************************************/ - -/** - * Close up the connection - * @param s Socket - * @return void - */ -void disconn(ano_socket_t s) -{ - shutdown(s, 2); - ano_sockclose(s); -} - -/*************************************************************************/ -/* Windows support functions */ - -#ifdef _WIN32 -/* Microsoft makes things nice and fun for us! */ -struct u_WSA_errors { - int error_code; - char *error_string; -}; - -/* Must be sorted ascending by error code */ -struct u_WSA_errors WSAErrors[] = { - {WSAEINTR, "Interrupted system call"}, - {WSAEBADF, "Bad file number"}, - {WSAEACCES, "Permission denied"}, - {WSAEFAULT, "Bad address"}, - {WSAEINVAL, "Invalid argument"}, - {WSAEMFILE, "Too many open sockets"}, - {WSAEWOULDBLOCK, "Operation would block"}, - {WSAEINPROGRESS, "Operation now in progress"}, - {WSAEALREADY, "Operation already in progress"}, - {WSAENOTSOCK, "Socket operation on non-socket"}, - {WSAEDESTADDRREQ, "Destination address required"}, - {WSAEMSGSIZE, "Message too long"}, - {WSAEPROTOTYPE, "Protocol wrong type for socket"}, - {WSAENOPROTOOPT, "Bad protocol option"}, - {WSAEPROTONOSUPPORT, "Protocol not supported"}, - {WSAESOCKTNOSUPPORT, "Socket type not supported"}, - {WSAEOPNOTSUPP, "Operation not supported on socket"}, - {WSAEPFNOSUPPORT, "Protocol family not supported"}, - {WSAEAFNOSUPPORT, "Address family not supported"}, - {WSAEADDRINUSE, "Address already in use"}, - {WSAEADDRNOTAVAIL, "Can't assign requested address"}, - {WSAENETDOWN, "Network is down"}, - {WSAENETUNREACH, "Network is unreachable"}, - {WSAENETRESET, "Net connection reset"}, - {WSAECONNABORTED, "Software caused connection abort"}, - {WSAECONNRESET, "Connection reset by peer"}, - {WSAENOBUFS, "No buffer space available"}, - {WSAEISCONN, "Socket is already connected"}, - {WSAENOTCONN, "Socket is not connected"}, - {WSAESHUTDOWN, "Can't send after socket shutdown"}, - {WSAETOOMANYREFS, "Too many references, can't splice"}, - {WSAETIMEDOUT, "Connection timed out"}, - {WSAECONNREFUSED, "Connection refused"}, - {WSAELOOP, "Too many levels of symbolic links"}, - {WSAENAMETOOLONG, "File name too long"}, - {WSAEHOSTDOWN, "Host is down"}, - {WSAEHOSTUNREACH, "No route to host"}, - {WSAENOTEMPTY, "Directory not empty"}, - {WSAEPROCLIM, "Too many processes"}, - {WSAEUSERS, "Too many users"}, - {WSAEDQUOT, "Disc quota exceeded"}, - {WSAESTALE, "Stale NFS file handle"}, - {WSAEREMOTE, "Too many levels of remote in path"}, - {WSASYSNOTREADY, "Network subsystem is unavailable"}, - {WSAVERNOTSUPPORTED, "Winsock version not supported"}, - {WSANOTINITIALISED, "Winsock not yet initialized"}, - {WSAHOST_NOT_FOUND, "Host not found"}, - {WSATRY_AGAIN, "Non-authoritative host not found"}, - {WSANO_RECOVERY, "Non-recoverable errors"}, - {WSANO_DATA, "Valid name, no data record of requested type"}, - {WSAEDISCON, "Graceful disconnect in progress"}, -#ifdef WSASYSCALLFAILURE - {WSASYSCALLFAILURE, "System call failure"}, -#endif - {0, NULL} -}; - -char *ano_sockstrerror(int error) -{ - static char unkerr[64]; - int start = 0; - int stop = sizeof(WSAErrors) / sizeof(WSAErrors[0]) - 1; - int mid; - - /* Microsoft decided not to use sequential numbers for the error codes, - * so we can't just use the array index for the code. But, at least - * use a binary search to make it as fast as possible. - */ - while (start <= stop) { - mid = (start + stop) / 2; - if (WSAErrors[mid].error_code > error) - stop = mid - 1; - - else if (WSAErrors[mid].error_code < error) - start = mid + 1; - else - return WSAErrors[mid].error_string; - } - sprintf(unkerr, "Unknown Error: %d", error); - return unkerr; -} - -int ano_socksetnonb(ano_socket_t fd) -{ - u_long i = 1; - return (!ioctlsocket(fd, FIONBIO, &i) ? -1 : 1); -} -#endif diff --git a/src/windows.cpp b/src/windows.cpp index 988256838..cfaaa216e 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -13,7 +13,7 @@ */ #ifdef WIN32 -#include <windows.h> +#include "services.h" const char *dlerror() { @@ -24,4 +24,87 @@ const char *dlerror() FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, errbuf, 512, NULL); return errbuf; } + + +/** This is inet_pton, but it works on Windows + * @param af The protocol type, AF_INET or AF_INET6 + * @param src The address + * @param dst Struct to put results in + * @return 1 on sucess, -1 on error + */ +int inet_pton(int af, const char *src, void *dst) +{ + int address_length; + sockaddr_storage sa; + sockaddr_in *sin = static_cast<sockaddr_in *>(&sa); + sockaddr_in6 *sin6 = static_cast<sockaddr_in6 *>(&sa); + + switch (af) + { + case AF_INET: + address_length = sizeof(sockaddr_in); + break; + case AF_INET6: + address_length = sizeof(sockaddr_in6); + break; + default: + return -1; + } + + if (WSAStringToAddress(static_cast<LPTSTR>(src), af, NULL, static_cast<LPSOCKADDR>(&sa), &address_length) == 0) + { + switch (af) + { + case AF_INET: + memcpy(dst, &sin->sin_addr, sizeof(in_addr)); + break; + case AF_INET6: + memcpy(dst, &sin6->sin6_addr, sizeof(in6_addr)); + break; + } + return 1; + } + + return 0; +} + +/** This is inet_ntop, but it works on Windows + * @param af The protocol type, AF_INET or AF_INET6 + * @param src Network address structure + * @param dst After converting put it here + * @param size sizeof the dest + * @return dst + */ +const char *inet_ntop(int af, const void *src, char *dst, size_t size) +{ + int address_length; + DWORD string_length = size; + sockaddr_storage sa; + sockaddr_in *sin = static_cast<sockaddr_in *>(&sa); + sockaddr_in6 *sin6 = static_cast<sockaddr_in6 *>(&sa); + + memset(&sa, 0, sizeof(sa)); + + switch (af) + { + case AF_INET: + address_length = sizeof(sockaddr_in); + sin->sin_family = af; + memcpy(&sin->sin_addr, src, sizeof(in_addr)); + break; + case AF_INET6: + address_length = sizeof(sockaddr_in6); + sin6->sin6_family = af; + memcpy(&sin6->sin6_addr, src, sizeof(in6_addr)); + break; + default: + return NULL; + } + + if (WSAAddressToString(static_cast<LPSOCKADDR>(&sa), address_length, NULL, dst, &string_length) == 0) + return dst; + + return NULL; +} + #endif |