/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2021 Dominic Hamon * Copyright (C) 2020-2023 Sadie Powell * Copyright (C) 2019 Robby * Copyright (C) 2013, 2016 Attila Molnar * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once class StreamSocket; class IOHookProvider : public std::enable_shared_from_this , public ServiceProvider { const bool middlehook; public: enum Type { IOH_UNKNOWN, IOH_SSL }; const Type type; /** Constructor * @param mod Module that owns the IOHookProvider * @param Name Name of the provider * @param hooktype One of IOHookProvider::Type * @param middle True if the IOHook instances created by this hook are subclasses of IOHookMiddle, false otherwise */ IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN, bool middle = false) : ServiceProvider(mod, Name, SERVICE_IOHOOK) , middlehook(middle) , type(hooktype) { } /** Check if the IOHook provided can appear in the non-last position of a hook chain. * That is the case if and only if the IOHook instances created are subclasses of IOHookMiddle. * @return True if the IOHooks provided are subclasses of IOHookMiddle */ bool IsMiddle() const { return middlehook; } /** Called when the provider should hook an incoming connection and act as being on the server-side of the connection. * This occurs when a bind block has a hook configured and the listener accepts a connection. * @param sock Socket to hook * @param client Client IP address and port * @param server Server IP address and port */ virtual void OnAccept(StreamSocket* sock, const irc::sockets::sockaddrs& client, const irc::sockets::sockaddrs& server) = 0; /** Called when the provider should hook an outgoing connection and act as being on the client side of the connection. * @param sock Socket to hook */ virtual void OnConnect(StreamSocket* sock) = 0; }; class IOHook : public Cullable { public: /** The IOHookProvider for this hook, contains information about the hook, * such as the module providing it and the hook type. */ std::shared_ptr prov; /** Constructor * @param provider IOHookProvider that creates this object */ IOHook(const std::shared_ptr& provider) : prov(provider) { } /** Determines whether this I/O hook is ready to send and receive data. */ virtual bool IsHookReady() const { return true; } /** * Called when the hooked socket has data to write, or when the socket engine returns it as writable * @param sock Hooked socket * @param sendq Send queue to send data from * @return 1 if the sendq has been completely emptied, 0 if there is * still data to send, and -1 if there was an error */ virtual ssize_t OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) = 0; /** Called immediately before the hooked socket is closed. When this event is called, shutdown() * has not yet been called on the socket. * @param sock Hooked socket */ virtual void OnStreamSocketClose(StreamSocket* sock) = 0; /** * Called when the hooked socket has data to read * @param sock Hooked socket * @param recvq The receive queue that new data should be appended to * @return 1 if new data has been read, 0 if no new data is ready (but the * socket is still connected), -1 if there was an error or close */ virtual ssize_t OnStreamSocketRead(StreamSocket* sock, std::string& recvq) = 0; /** Sends a ping to the remote client to check whether it is still connected. * @return True if the client was pinged; otherwise, false. */ virtual bool Ping() { return false; } }; class IOHookMiddle : public IOHook { /** Data already processed by the IOHook waiting to go down the chain */ StreamSocket::SendQueue sendq; /** Data waiting to go up the chain */ std::string precvq; /** Next IOHook in the chain */ IOHook* nexthook = nullptr; protected: /** Get all queued up data which has not yet been passed up the hook chain * @return RecvQ containing the data */ std::string& GetRecvQ() { return precvq; } /** Get all queued up data which is ready to go down the hook chain * @return SendQueue containing all data waiting to go down the hook chain */ StreamSocket::SendQueue& GetSendQ() { return sendq; } public: /** Constructor * @param provider IOHookProvider that creates this object */ IOHookMiddle(const std::shared_ptr& provider) : IOHook(provider) { } /** @copydoc IOHook::OnStreamSocketClose */ void OnStreamSocketClose(StreamSocket* sock) override { } /** Get all queued up data which is ready to go down the hook chain * @return SendQueue containing all data waiting to go down the hook chain */ const StreamSocket::SendQueue& GetSendQ() const { return sendq; } /** Get the next IOHook in the chain * @return Next hook in the chain or NULL if this is the last hook */ IOHook* GetNextHook() const { return nexthook; } /** Set the next hook in the chain * @param hook Hook to set as the next hook in the chain */ void SetNextHook(IOHook* hook) { nexthook = hook; } /** Check if a hook is capable of being the non-last hook in a hook chain and if so, cast it to an IOHookMiddle object. * @param hook IOHook to check * @return IOHookMiddle referring to the same hook or NULL */ static IOHookMiddle* ToMiddleHook(IOHook* hook) { if (hook->prov->IsMiddle()) return static_cast(hook); return nullptr; } friend class StreamSocket; };