/* * * (C) 2003-2022 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. */ #ifndef SOCKETS_H #define SOCKETS_H #ifndef _WIN32 #include #include #include #endif #include "anope.h" #define NET_BUFSIZE 65535 /** A sockaddr union used to combine IPv4 and IPv6 sockaddrs */ union CoreExport sockaddrs { sockaddr sa; sockaddr_in sa4; sockaddr_in6 sa6; /** Construct the object, sets everything to 0 */ sockaddrs(const Anope::string &address = ""); /** Memset the object to 0 */ void clear(); /** Get address family. */ int family() const; /** Get the size of the sockaddr we represent * @return The size */ size_t size() const; /** Get the port represented by this addr * @return The port, or -1 on fail */ int port() const; /** Get the address represented by this addr * @return The address */ Anope::string addr() const; /** Get the reverse address represented by this addr * @return The reverse address */ Anope::string reverse() const; /* Is this address ipv6? */ bool ipv6() const; /** Check if this sockaddr has data in it */ bool valid() const; /** Compares with sockaddr with another. Compares address type, port, and address * @return true if they are the same */ bool operator==(const sockaddrs &other) const; /* The same as above but not */ inline bool operator!=(const sockaddrs &other) const { return !(*this == other); } /** The equivalent of inet_pton * @param type AF_INET or AF_INET6 * @param address The address to place in the sockaddr structures * @param pport An option port to include in the sockaddr structures * @throws A socket exception if given invalid IPs */ void pton(int type, const Anope::string &address, int pport = 0); /** The equivalent of inet_ntop * @param type AF_INET or AF_INET6 * @param address The in_addr or in_addr6 structure * @throws A socket exception if given an invalid structure */ void ntop(int type, const void *src); }; class CoreExport cidr { sockaddrs addr; Anope::string cidr_ip; unsigned short cidr_len; public: cidr(const Anope::string &ip); cidr(const Anope::string &ip, unsigned char len); cidr(const sockaddrs &ip, unsigned char len); Anope::string mask() const; bool match(const sockaddrs &other); bool valid() const; bool operator<(const cidr &other) const; bool operator==(const cidr &other) const; bool operator!=(const cidr &other) const; struct CoreExport hash { size_t operator()(const cidr &s) const; }; }; class SocketException : public CoreException { public: /** Constructor for socket exceptions * @param message Error message */ SocketException(const Anope::string &message) : CoreException(message) { } /** Destructor * @throws Nothing */ virtual ~SocketException() throw() { } }; enum SocketFlag { SF_DEAD = 1, SF_READABLE, SF_WRITABLE, SF_CONNECTING, SF_CONNECTED, SF_ACCEPTING, SF_ACCEPTED, SF_SIZE }; class CoreExport SocketIO { public: virtual ~SocketIO() { } /** Receive something from the buffer * @param s The socket * @param buf The buf to read to * @param sz How much to read * @return Number of bytes received */ virtual int Recv(Socket *s, char *buf, size_t sz); /** Write something to the socket * @param s The socket * @param buf The data to write * @param size The length of the data */ virtual int Send(Socket *s, const char *buf, size_t sz); int Send(Socket *s, const Anope::string &buf); /** Accept a connection from a socket * @param s The socket * @return The new socket */ virtual ClientSocket *Accept(ListenSocket *s); /** Finished accepting a connection from a socket * @param s The socket * @return SF_ACCEPTED if accepted, SF_ACCEPTING if still in process, SF_DEAD on error */ virtual SocketFlag FinishAccept(ClientSocket *cs); /** Bind a socket * @param s The socket * @param ip The IP to bind to * @param port The optional port to bind to */ virtual void Bind(Socket *s, const Anope::string &ip, int port = 0); /** Connect the socket * @param s The socket * @param target IP to connect to * @param port to connect to */ virtual void Connect(ConnectionSocket *s, const Anope::string &target, int port); /** Called to potentially finish a pending connection * @param s The socket * @return SF_CONNECTED on success, SF_CONNECTING if still pending, and SF_DEAD on error. */ virtual SocketFlag FinishConnect(ConnectionSocket *s); /** Called when the socket is destructing */ virtual void Destroy() { } }; class CoreExport Socket { protected: /* Socket FD */ int sock; /* Is this an IPv6 socket? */ bool ipv6; public: std::bitset flags; /* Sockaddrs for bind() (if it's bound) */ sockaddrs bindaddr; /* I/O functions used for this socket */ SocketIO *io; /** Empty constructor, should not be called. */ Socket(); /** Constructor, possibly creates the socket and adds it to the engine * @param sock The socket to use, -1 if we need to create our own * @param ipv6 true if using ipv6 * @param type The socket type, defaults to SOCK_STREAM */ Socket(int sock, bool ipv6 = false, int type = SOCK_STREAM); /** Destructor, closes the socket and removes it from the engine */ virtual ~Socket(); /** Get the socket FD for this socket * @return the fd */ int GetFD() const; /** Check if this socket is IPv6 * @return true or false */ bool IsIPv6() const; /** Mark a socket as (non)blocking * @param state true to enable blocking, false to disable blocking * @return true if the socket is now blocking */ bool SetBlocking(bool state); /** Bind the socket to an ip and port * @param ip The ip * @param port The port */ void Bind(const Anope::string &ip, int port = 0); /** Called when there either is a read or write event. * @return true to continue to call ProcessRead/ProcessWrite, false to not continue */ virtual bool Process(); /** Called when there is something to be received for this socket * @return true on success, false to drop this socket */ virtual bool ProcessRead(); /** Called when the socket is ready to be written to * @return true on success, false to drop this socket */ virtual bool ProcessWrite(); /** Called when there is an error for this socket * @return true on success, false to drop this socket */ virtual void ProcessError(); }; class CoreExport BufferedSocket : public virtual Socket { protected: /* Things read from the socket */ Anope::string read_buffer; /* Things to be written to the socket */ Anope::string write_buffer; /* How much data was received from this socket on this recv() */ int recv_len; public: BufferedSocket(); virtual ~BufferedSocket(); /** Called when there is something to be received for this socket * @return true on success, false to drop this socket */ bool ProcessRead() anope_override; /** Called when the socket is ready to be written to * @return true on success, false to drop this socket */ bool ProcessWrite() anope_override; /** Gets the new line from the input buffer, if any */ const Anope::string GetLine(); /** Write to the socket * @param message The message */ protected: virtual void Write(const char *buffer, size_t l); public: void Write(const char *message, ...); void Write(const Anope::string &message); /** Get the length of the read buffer * @return The length of the read buffer */ int ReadBufferLen() const; /** Get the length of the write buffer * @return The length of the write buffer */ int WriteBufferLen() const; }; class CoreExport BinarySocket : public virtual Socket { protected: struct DataBlock { char *orig; char *buf; size_t len; DataBlock(const char *b, size_t l); ~DataBlock(); }; /* Data to be written out */ std::deque write_buffer; public: BinarySocket(); virtual ~BinarySocket(); /** Called when there is something to be received for this socket * @return true on success, false to drop this socket */ bool ProcessRead() anope_override; /** Called when the socket is ready to be written to * @return true on success, false to drop this socket */ bool ProcessWrite() anope_override; /** Write data to the socket * @param buffer The data to write * @param l The length of the data; if 0 then this function returns without doing anything */ virtual void Write(const char *buffer, size_t l); void Write(const char *message, ...); void Write(const Anope::string &message); /** Called with data from the socket * @param buffer The data * @param l The length of buffer * @return true to continue reading, false to drop the socket */ virtual bool Read(const char *buffer, size_t l); }; class CoreExport ListenSocket : public virtual Socket { public: /** Constructor * @param bindip The IP to bind to * @param port The port to listen on * @param ipv6 true for ipv6 */ ListenSocket(const Anope::string &bindip, int port, bool ipv6); virtual ~ListenSocket(); /** Process what has come in from the connection * @return false to destroy this socket */ bool ProcessRead(); /** Called when a connection is accepted * @param fd The FD for the new connection * @param addr The sockaddr for where the connection came from * @return The new socket */ virtual ClientSocket *OnAccept(int fd, const sockaddrs &addr) = 0; }; class CoreExport ConnectionSocket : public virtual Socket { public: /* Sockaddrs for connection ip/port */ sockaddrs conaddr; /** Connect the socket * @param TargetHost The target host to connect to * @param Port The target port to connect to */ void Connect(const Anope::string &TargetHost, int Port); /** Called when there either is a read or write event. * Used to determine whether or not this socket is connected yet. * @return true to continue to call ProcessRead/ProcessWrite, false to not continue */ bool Process() anope_override; /** Called when there is an error for this socket * @return true on success, false to drop this socket */ void ProcessError() anope_override; /** Called on a successful connect */ virtual void OnConnect(); /** Called when a connection is not successful * @param error The error */ virtual void OnError(const Anope::string &error); }; class CoreExport ClientSocket : public virtual Socket { public: /* Listen socket this connection came from */ ListenSocket *ls; /* Clients address */ sockaddrs clientaddr; /** Constructor * @param ls Listen socket this connection is from * @param addr Address the connection came from */ ClientSocket(ListenSocket *ls, const sockaddrs &addr); /** Called when there either is a read or write event. * Used to determine whether or not this socket is connected yet. * @return true to continue to call ProcessRead/ProcessWrite, false to not continue */ bool Process() anope_override; /** Called when there is an error for this socket * @return true on success, false to drop this socket */ void ProcessError() anope_override; /** Called when a client has been accepted() successfully. */ virtual void OnAccept(); /** Called when there was an error accepting the client */ virtual void OnError(const Anope::string &error); }; class CoreExport Pipe : public Socket { public: /** The FD of the write pipe * this->sock is the readfd */ int write_pipe; Pipe(); ~Pipe(); /** Called when data is to be read, reads the data then calls OnNotify */ bool ProcessRead() anope_override; /** Write data to this pipe * @param data The data to write * @param sz The amount of data to wirite */ void Write(const char *data, size_t sz); inline void Write(const Anope::string &data) { this->Write(data.c_str(), data.length() + 1); } /** Read data from this pipe * @param data A buffer to read data into * @param sz The size of the buffer * @return The amount of data read */ int Read(char *data, size_t sz); /** Mark the write end of this pipe (non)blocking * @param state true to enable blocking, false to disable blocking * @return true if the socket is now blocking */ bool SetWriteBlocking(bool state); /** Called when this pipe needs to be woken up * Is the same as Write("\0", 1) */ void Notify(); /** Called after ProcessRead comes back from Notify(), overload to do something useful */ virtual void OnNotify() = 0; }; extern CoreExport uint32_t TotalRead; extern CoreExport uint32_t TotalWritten; extern CoreExport SocketIO NormalSocketIO; #endif // SOCKET_H