summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes.conf4
-rw-r--r--TODO16
-rw-r--r--data/example.conf38
-rw-r--r--include/config.h6
-rw-r--r--include/configreader.h8
-rw-r--r--include/extern.h28
-rw-r--r--include/modules.h10
-rw-r--r--include/services.h155
-rw-r--r--include/sockets.h191
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Makefile4
-rw-r--r--src/config.c39
-rw-r--r--src/core/cs_xop.c19
-rw-r--r--src/core/db_plain.cpp2
-rw-r--r--src/core/os_shutdown.c3
-rw-r--r--src/core/os_stats.c4
-rw-r--r--src/init.c55
-rw-r--r--src/log.c4
-rw-r--r--src/main.c178
-rw-r--r--src/messages.c3
-rw-r--r--src/process.c11
-rw-r--r--src/protocol/bahamut.c2
-rw-r--r--src/protocol/inspircd11.c2
-rw-r--r--src/protocol/unreal32.c5
-rw-r--r--src/send.c8
-rw-r--r--src/servers.c9
-rw-r--r--src/sockets.cpp459
-rw-r--r--src/sockutil.c718
-rw-r--r--src/windows.cpp85
29 files changed, 1011 insertions, 1059 deletions
diff --git a/Changes.conf b/Changes.conf
index 2c3d33ceb..9174eb41f 100644
--- a/Changes.conf
+++ b/Changes.conf
@@ -9,6 +9,9 @@ options:userlen added to configure maxiumum ident length
options:hostlen added to configure maximum hostname length
db_plain:database added to configure what database file to use
options:passlen added to specify the maximum length of passwords
+uplink:ipv6 added to enable IPv6 connectivity
+options:maxretries added to specify the number of reconnect attempts allowed
+options:retrywait added to specify how long to wait between reconnect attempts
** MODIFIED CONFIGURATION DIRECTIVES **
options:encryption added enc_sha256
@@ -26,6 +29,7 @@ operserv:newsdatabase deleted because of the new database system
operserv:exceptiondatabase deleted because of the new database system
hs_request:database deleted because of the new database system
os_ignore:database deleted because of the new database system
+serverinfo:localport deleted
Anope Version 1.9.1
-------------------
diff --git a/TODO b/TODO
index 222ba6a18..9505bc8de 100644
--- a/TODO
+++ b/TODO
@@ -14,11 +14,10 @@ Legend:
[+] New database format (text, not binary - works very well for merging and so on)
[x] generic database routines modules can use to create their own database
[?] IRCd capability support: don't rely on CAPAB, provide an interface to turn capabilities on specifically
-[ ] Socket subsystem needs some serious loving
- [ ] Multiple sockets
- [ ] Asynchronous, using select() (multiple engines? not really needed..)
- [ ] Callbacks, event style, see also inspircd
- [ ] Asynchronous DNS?
+[x] Socket subsystem needs some serious loving
+ [x] Multiple sockets
+ [x] Asynchronous, using select() (multiple engines? not really needed..)
+ [x] Callbacks, event style, see also inspircd
[x] generic way to check which modes a user has set (u->HasUmode(UMODE_OPER))
[?] way for one module to depend on another... not like 2 MOD_HEADs and it being unpredictable which is loaded first..
the MOD_HEAD MOD_TAIL allows for too few combinations (interface code of insp, hooks code of insp?)
@@ -34,13 +33,15 @@ Legend:
Future
------
+[ ] Asynchronous DNS
+[ ] CIDR Akills, session exceptions, etc
+[ ] Hashing system for storing just about everything needs to die
[ ] Move a (lot) of stuff to class members as a defined interface rather than copypasta everywhere (ns_set, ns_saset!)
[?] Remote identification (1.9.1? will this break stuff?)
[ ] Language charset stuff, including collation (1.9.1? phoenix?)
[ ] Add support for +k, +q, etc type umodes
[ ] fantasy: allow replies/notifications to fantasy commands to go to the channel via notice
[?] a way for a module to queue itself (or even another module) for unloading
-[ ] add overridden form of SendGlobops accepting BotInfo
[ ] Language system is disgusting, it must die.
[ ] Modules should also have a way to add strings programatically
[ ] Should be able to add many strings by dropping a file in a set location.
@@ -71,8 +72,7 @@ Future
[ ] Reason
[ ] Unique IDs on each AKILL/blah so that networks may use them as ticket IDs
[ ] HS ACTIVATE -ALL (rob sez this all needs reviewing)
-[ ] NS MARK, CS MARK. Allow multiple marks. Combine into OS MARK?
-[ ] Method to list suspended/forbidden nicks/channels?
+[?] NS MARK, CS MARK. Allow multiple marks. Combine into OS MARK? (AKA os_info)
[?] MS IGNORE. Make it take nick (accounts) or n!u@h masks. Fake success of memo send still, but send to opers?
[?] More "friendly" date displays of some things like registration time ("November 7th 2006 (2 years, 0 months, 0 days ago)")
[?] OS INJECT
diff --git a/data/example.conf b/data/example.conf
index 3cc20ad31..c372ae17d 100644
--- a/data/example.conf
+++ b/data/example.conf
@@ -91,6 +91,11 @@ uplink
host = "localhost"
/*
+ * Enable if Services should connect using IPv6
+ */
+ ipv6 = no
+
+ /*
* The port to connect to.
* The IRCd *MUST* be configured to listen on this port, and to accept
* server connections.
@@ -132,17 +137,13 @@ serverinfo
/*
* The local address that Services will bind to before connecting to the remote
- * server. This may be useful for multihomed hosts. If neither of these are given,
- * Services will let the Operating System choose the local address. If only a
- * hostname is specified, Services will bind to that address but let the Operating
- * System choose the local port. These directives are optional, and may be overridden
- * with the -local command-line option when starting Services.
+ * server. This may be useful for multihomed hosts. If ommited, Services will let
+ * the Operating System choose the local address. This directive is optional.
*
- * If you don't know what this means or don't need to use it, just leave these
+ * If you don't know what this means or don't need to use it, just leave this
* directives commented out.
*/
#localhost = "nowhere."
- #localport = 0
/*
* This directive instructs Anope which IRCd Protocol to speak when connecting.
@@ -532,15 +533,26 @@ options
*/
#enablelogchannel = yes
- /*
- * Default modes for mode lock, these are set on newly registered channels.
- */
+ /*
+ * Default modes for mode lock, these are set on newly registered channels.
+ */
mlock = "+nrt"
- /*
- * Modes to set on service bots when they join channels, comment this out for no modes
- */
+ /*
+ * Modes to set on service bots when they join channels, comment this out for no modes
+ */
botmodes = "ao"
+
+ /*
+ * How many times services should attempt to reconnect to the uplink before giving up
+ * Comment this out to never give up.
+ */
+ maxretries = 10
+
+ /*
+ * How long to wait between connection retries, in seconds.
+ */
+ retrywait = 60
}
diff --git a/include/config.h b/include/config.h
index a43b17f3e..1aaf5a47c 100644
--- a/include/config.h
+++ b/include/config.h
@@ -25,9 +25,6 @@
/* Name of log file (in Services directory) */
#define LOG_FILENAME "services.log"
-/* Maximum amount of data from/to the network to buffer (bytes). */
-#define NET_BUFSIZE 65536
-
/******************* END OF USER-CONFIGURABLE SECTION ********************/
/* Size of input buffer (note: this is different from BUFSIZ)
@@ -35,6 +32,9 @@
* things will happen. */
#define BUFSIZE 1024
+/* Maximum amount of data from/to the network to buffer (bytes). */
+#define NET_BUFSIZE 65536
+
/**************************************************************************/
#endif /* CONFIG_H */
diff --git a/include/configreader.h b/include/configreader.h
index a461c1f15..c6dbbefb8 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -384,8 +384,6 @@ class ServerConfig
/* Host to connect to **/
char *LocalHost;
- /* Port */
- unsigned LocalPort;
/* List of uplink servers to try and connect to */
std::list<Uplink *> Uplinks;
@@ -475,7 +473,7 @@ class ServerConfig
bool ForceForbidReason;
/* Services should use privmsgs instead of notices */
bool UsePrivmsg;
- /* Services only respond to full PRIVMSG client@services.server.name messaegs */
+ /* Services only respond to full PRIVMSG client@services.server.name messages */
bool UseStrictPrivMsg;
/* Dump a core file if we crash */
bool DumpCore;
@@ -490,6 +488,10 @@ class ServerConfig
std::string MLock;
/* Default botmodes on channels, defaults to ao */
std::string BotModes;
+ /* How many times to try and reconnect to the uplink before giving up */
+ unsigned MaxRetries;
+ /* How long to wait between connection attempts */
+ int RetryWait;
/* Services can use email */
bool UseMail;
diff --git a/include/extern.h b/include/extern.h
index ccff00e58..983f61af5 100644
--- a/include/extern.h
+++ b/include/extern.h
@@ -244,14 +244,14 @@ E int protocoldebug;
E int is44;
E int quitting;
-E int delayed_quit;
+E int shutting_down;
E const char *quitmsg;
-E char inbuf[BUFSIZE];
-E int servsock;
E int save_data;
E int got_alarm;
E time_t start_time;
+E Socket *UplinkSock;
+
E void save_databases();
E void expire_all();
E void sighandler(int signum);
@@ -455,7 +455,7 @@ E int delete_ignore(const char *nick);
E int clear_ignores();
E int split_buf(char *buf, const char ***argv, int colon_special);
-E void process();
+E void process(const std::string &buf);
/**** send.c ****/
@@ -485,7 +485,7 @@ E void CapabParse(int ac, const char **av);
E int is_ulined(const char *server);
E int is_sync(Server *server);
-E Server *new_server(Server * uplink, const char *name, const char *desc, ServerFlag flag, const char *suid);
+E Server *new_server(Server * uplink, const char *name, const char *desc, ServerFlag flag, const std::string &suid);
E Server *findserver(Server *s, const char *name);
@@ -538,20 +538,10 @@ E void slist_pack(SList *slist);
E int slist_remove(SList *slist, void *item);
E int slist_setcapacity(SList *slist, int16 capacity);
-/**** sockutil.c ****/
-
-E int32 total_read, total_written;
-E int32 read_buffer_len();
-E int32 write_buffer_len();
-
-E int sgetc(ano_socket_t s);
-E char *sgets(char *buf, int len, ano_socket_t s);
-E char *sgets2(char *buf, int len, ano_socket_t s);
-E int sread(ano_socket_t s, char *buf, int len);
-E int sputs(char *str, ano_socket_t s);
-E int sockprintf(ano_socket_t s, const char *fmt, ...);
-E int conn(const char *host, int port, const char *lhost, int lport);
-E void disconn(ano_socket_t s);
+/**** sockets.cpp ****/
+E SocketEngine socketEngine;
+E int32 TotalRead;
+E int32 TotalWritten;
/**** users.c ****/
diff --git a/include/modules.h b/include/modules.h
index bf178d72d..88a605acd 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -671,10 +671,14 @@ class CoreExport Module
*/
virtual void OnPreServerConnect() { }
- /** Called when anope connects to its uplink
+ /** Called when Anope connects to its uplink
*/
virtual void OnServerConnect() { }
+ /** Called when Anope disconnects from its uplink, before it tries to reconnect
+ */
+ virtual void OnServerDisconnect() { }
+
/** Called before the database expire routines are called
* Note: Code that is in seperate expiry routines should just be done
* when we save the DB, theres no need to have both
@@ -1202,8 +1206,8 @@ enum Implementation
I_OnModuleLoad, I_OnModuleUnload,
/* Other */
- I_OnReload, I_OnPreServerConnect, I_OnNewServer, I_OnServerConnect, I_OnPreCommandRun, I_OnPreCommand, I_OnPostCommand,
- I_OnPreDatabaseExpire, I_OnPreRestart, I_OnRestart, I_OnPreShutdown, I_OnShutdown, I_OnSignal,
+ I_OnReload, I_OnPreServerConnect, I_OnNewServer, I_OnServerConnect, I_OnServerDisconnect, I_OnPreCommandRun, I_OnPreCommand,
+ I_OnPostCommand, I_OnPreDatabaseExpire, I_OnPreRestart, I_OnRestart, I_OnPreShutdown, I_OnShutdown, I_OnSignal,
I_OnServerQuit, I_OnTopicUpdated,
I_OnEncrypt, I_OnEncryptInPlace, I_OnEncryptCheckLen, I_OnDecrypt, I_OnCheckPassword,
I_OnChannelModeSet, I_OnChannelModeUnset, I_OnUserModeSet, I_OnUserModeUnset, I_OnChannelModeAdd, I_OnUserModeAdd,
diff --git a/include/services.h b/include/services.h
index 7e6783404..ea6b89364 100644
--- a/include/services.h
+++ b/include/services.h
@@ -19,28 +19,23 @@
#include "sysconf.h"
#include "config.h"
-#ifndef MAX_CMD_HASH
-#define MAX_CMD_HASH 1024
-#endif
-
/* Some SUN fixs */
#ifdef __sun
-/* Solaris specific code, types that do not exist in Solaris'
- * sys/types.h
- **/
-#undef u_int8_t
-#undef u_int16_t
-#undef u_int32_t
-#undef u_int_64_t
-#define u_int8_t uint8_t
-#define u_int16_t uint16_t
-#define u_int32_t uint32_t
-#define u_int64_t uint64_t
-
-#ifndef INADDR_NONE
-#define INADDR_NONE (-1)
-#endif
-
+ /* Solaris specific code, types that do not exist in Solaris'
+ * sys/types.h
+ **/
+# undef u_int8_t
+# undef u_int16_t
+# undef u_int32_t
+# undef u_int_64_t
+# define u_int8_t uint8_t
+# define u_int16_t uint16_t
+# define u_int32_t uint32_t
+# define u_int64_t uint64_t
+
+# ifndef INADDR_NONE
+# define INADDR_NONE (-1)
+# endif
#endif
@@ -49,63 +44,57 @@
#include <stdlib.h>
#include <string.h>
-/* Windows does not have: unistd.h, grp.h, netdb.h, netinet/in.h, sys/socket.h, sys/time.h
- * Windows requires: winsock.h
- * -- codemastr
- */
-
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
#include <signal.h>
#include <time.h>
#include <errno.h>
-
-#ifndef _WIN32
-#include <grp.h>
-#endif
-
#include <limits.h>
-#ifndef _WIN32
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#else
-#include <winsock.h>
-#include <windows.h>
-#endif
-
-#include <sys/stat.h> /* for umask() on some systems */
+#include <sys/stat.h> /* for umask() on some systems */
#include <sys/types.h>
-#ifdef HAVE_GETTIMEOFDAY
-#include <sys/time.h>
-#endif
-
-#ifdef _WIN32
-#include <sys/timeb.h>
-#include <direct.h>
-#include <io.h>
-#endif
-
#include <fcntl.h>
#ifndef _WIN32
-#ifdef HAVE_BACKTRACE
-#include <execinfo.h>
-#endif
-#endif
-
-#ifndef _WIN32
-#include <dirent.h>
+# include <unistd.h>
+# include <grp.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <dirent.h>
+# ifdef HAVE_BACKTRACE
+# include <execinfo.h>
+# endif
+# define DllExport
+# define CoreExport
+# define MARK_DEPRECATED __attribute((deprecated))
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <windows.h>
+# include <sys/timeb.h>
+# include <direct.h>
+# include <io.h>
+# ifdef MODULE_COMPILE
+# define CoreExport __declspec(dllimport)
+# define DllExport __declspec(dllexport)
+# else
+# define CoreExport __declspec(dllexport)
+# define DllExport __declspec(dllimport)
+# endif
+/* VS2008 hates having this define before its own */
+# define vsnprintf _vsnprintf
+/* We have our own inet_pton and inet_ntop (Windows doesn't have its own) */
+# define inet_pton inet_pton_
+# define inet_ntop inet_ntop_
+# define MARK_DEPRECATED
+
+extern CoreExport int inet_pton(int af, const char *src, void *dst);
+extern CoreExport const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
-#ifdef _WIN32
-/* VS2008 hates having this define before its own */
-#define vsnprintf _vsnprintf
+#ifdef HAVE_GETTIMEOFDAY
+# include <sys/time.h>
#endif
#if HAVE_STRINGS_H
@@ -116,8 +105,6 @@
# include <sys/select.h>
#endif
-#include "sockets.h"
-
#ifndef va_copy
# ifdef __va_copy
# define VA_COPY(DEST,SRC) __va_copy((DEST),(SRC))
@@ -168,25 +155,6 @@ extern int strncasecmp(const char *, const char *, size_t);
# undef int32
#endif
-#ifndef _WIN32
- #define MARK_DEPRECATED __attribute((deprecated))
-#else
- #define MARK_DEPRECATED
-#endif
-
-#ifdef _WIN32
-# ifdef MODULE_COMPILE
-# define CoreExport __declspec(dllimport)
-# define DllExport __declspec(dllexport)
-# else
-# define CoreExport __declspec(dllexport)
-# define DllExport __declspec(dllimport)
-# endif
-#else
-# define DllExport
-# define CoreExport
-#endif
-
/** This definition is used as shorthand for the various classes
* and functions needed to make a module loadable by the OS.
* It defines the class factory and external init_module function.
@@ -230,7 +198,7 @@ extern int strncasecmp(const char *, const char *, size_t);
#include "defs.h"
#include "slist.h"
-/* pull in the various bits of STL to pull in */
+/* Pull in the various bits of STL */
#include <iostream>
#include <string>
#include <map>
@@ -239,6 +207,7 @@ extern int strncasecmp(const char *, const char *, size_t);
#include <vector>
#include <deque>
#include <bitset>
+#include <set>
/** This class can be used on its own to represent an exception, or derived to represent a module-specific exception.
* When a module whishes to abort, e.g. within a constructor, it should throw an exception using ModuleException or
@@ -302,6 +271,13 @@ class ModuleException : public CoreException
virtual ~ModuleException() throw() {}
};
+
+/*************************************************************************/
+
+#include "sockets.h"
+
+/*************************************************************************/
+
/** Class with the ability to keep flags on items, they should extend from this
* where T is an enum.
*/
@@ -1107,11 +1083,14 @@ struct Uplink {
char *host;
unsigned port;
char *password;
- Uplink(const char *_host, int _port, const char *_password)
+ bool ipv6;
+
+ Uplink(const char *_host, int _port, const char *_password, bool _ipv6)
{
host = sstrdup(_host);
port = _port;
password = sstrdup(_password);
+ ipv6 = _ipv6;
}
~Uplink()
{
diff --git a/include/sockets.h b/include/sockets.h
index c722fbb1e..3277c1b9f 100644
--- a/include/sockets.h
+++ b/include/sockets.h
@@ -15,34 +15,171 @@
#define SOCKETS_H
#ifdef _WIN32
-typedef SOCKET ano_socket_t;
-#define ano_sockread(fd, buf, len) recv(fd, buf, len, 0)
-#define ano_sockwrite(fd, buf, len) send(fd, buf, len, 0)
-#define ano_sockclose(fd) closesocket(fd)
-#define ano_sockgeterr() WSAGetLastError()
-#define ano_sockseterr(err) WSASetLastError(err)
-/* ano_sockstrerror in sockutil.c */
-extern char *ano_sockstrerror(int);
-/* ano_socksetnonb in sockutil.c */
-#define ano_sockerrnonb(err) (err == WSAEINPROGRESS || err == WSAEWOULDBLOCK)
-#define SOCKERR_EBADF WSAENOTSOCK
-#define SOCKERR_EINTR WSAEINTR
-#define SOCKERR_EINVAL WSAEINVAL
-#define SOCKERR_EINPROGRESS WSAEINPROGRESS
+#define CloseSocket closesocket
#else
-typedef int ano_socket_t;
-#define ano_sockread(fd, buf, len) read(fd, buf, len)
-#define ano_sockwrite(fd, buf, len) write(fd, buf, len)
-#define ano_sockclose(fd) close(fd)
-#define ano_sockgeterr() errno
-#define ano_sockseterr(err) errno = err
-#define ano_sockstrerror(err) strerror(err)
-#define ano_socksetnonb(fd) fcntl(fd, F_SETFL, O_NONBLOCK)
-#define ano_sockerrnonb(err) (err == EINPROGRESS)
-#define SOCKERR_EBADF EBADF
-#define SOCKERR_EINTR EINTR
-#define SOCKERR_EINVAL EINVAL
-#define SOCKERR_EINPROGRESS EINPROGRESS
+#define CloseSocket close
#endif
+class SocketException : public CoreException
+{
+ public:
+ /** Default constructor for socket exceptions
+ * @param message Error message
+ */
+ SocketException(const std::string &message) : CoreException(message) { }
+
+ /** Default destructor
+ * @throws Nothing
+ */
+ virtual ~SocketException() throw() { }
+};
+
+class CoreExport Socket
+{
+ private:
+ /** Read from the socket
+ * @param buf Buffer to read to
+ * @param sz How much to read
+ * @return Number of bytes recieved
+ */
+ virtual int RecvInternal(char *buf, size_t sz) const;
+
+ /** Write to the socket
+ * @param buf What to write
+ * @return Number of bytes sent, -1 on error
+ */
+ virtual int SendInternal(const std::string &buf) const;
+
+ protected:
+ /* Socket FD */
+ int Sock;
+ /* Host this socket is connected to */
+ std::string TargetHost;
+ /* Port we're connected to */
+ int Port;
+ /* IP this socket is bound to */
+ std::string BindHost;
+ /* Is this an IPv6 socket? */
+ bool IPv6;
+
+ /* Messages to be written to the socket */
+ std::string WriteBuffer;
+ /* Part of a message not totally yet recieved */
+ std::string extrabuf;
+ /* How much data was recieved from the socket */
+ size_t RecvLen;
+
+ public:
+ /** 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(const std::string &nTargetHost, int nPort, const std::string &nBindHost = "", bool nIPv6 = false);
+
+ /** Default destructor
+ */
+ virtual ~Socket();
+
+ /** Get the socket FD for this socket
+ * @return The fd
+ */
+ virtual int GetSock() const;
+
+ /** Check if this socket is IPv6
+ * @return true or false
+ */
+ bool IsIPv6() const;
+
+ /** Called when there is something to be read from thie socket
+ * @return true on success, false to kill this socket
+ */
+ virtual bool ProcessRead();
+
+ /** Called when this socket becomes writeable
+ * @return true on success, false to drop this socket
+ */
+ virtual bool ProcessWrite();
+
+ /** Called when there is an error on this socket
+ */
+ virtual void ProcessError();
+
+ /** Called with a message recieved from the socket
+ * @param buf The message
+ * @return true on success, false to kill this socket
+ */
+ virtual bool Read(const std::string &buf);
+
+ /** Write to the socket
+ * @param message The message to write
+ */
+ void Write(const char *message, ...);
+ void Write(std::string &message);
+
+ /** Get the length of the read buffer
+ * @return The length of the read buffer
+ */
+ size_t ReadBufferLen() const;
+
+ /** Get the length of the write buffer
+ * @return The length of the write buffer
+ */
+ size_t WriteBufferLen() const;
+};
+
+class CoreExport SocketEngine
+{
+ private:
+ /* List of sockets that need to be deleted */
+ std::set<Socket *> OldSockets;
+ /* FDs to read */
+ fd_set ReadFDs;
+ /* FDs that want writing */
+ fd_set WriteFDs;
+ /* Max FD */
+ int MaxFD;
+
+ /** Unmark a socket as writeable
+ * @param s The socket
+ */
+ void ClearWriteable(Socket *s);
+ public:
+ /* Set of sockets */
+ std::set<Socket *> Sockets;
+
+ /** Constructor
+ */
+ SocketEngine();
+
+ /** Destructor
+ */
+ virtual ~SocketEngine();
+
+ /** Add a socket to the socket engine
+ * @param s The socket
+ */
+ void AddSocket(Socket *s);
+
+ /** Delete a socket from the socket engine
+ * @param s The socket
+ */
+ void DelSocket(Socket *s);
+
+ /** Mark a socket as wanting to be written to
+ * @param s The socket
+ */
+ void MarkWriteable(Socket *s);
+
+ /** Called to iterate through each socket and check for activity
+ */
+ void Process();
+
+ /** Get the last socket error
+ * @return The error
+ */
+ const std::string GetError() const;
+};
+
#endif
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;
}
diff --git a/src/log.c b/src/log.c
index a0cb38d59..41472e7ad 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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