From: Attila Molnar Date: Sun, 10 May 2015 17:20:06 +0000 (+0200) Subject: Merge insp20 X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=6fe1f4e1136f2ab95a88e68af1894bf6002d03f4;p=user%2Fhenk%2Fcode%2Finspircd.git Merge insp20 --- 6fe1f4e1136f2ab95a88e68af1894bf6002d03f4 diff --cc src/modules/extra/m_ssl_gnutls.cpp index a2bdb76ee,59ac1acb3..d33403aba --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@@ -777,433 -969,18 +777,446 @@@ info_done_dealloc gnutls_x509_crt_deinit(cert); } - void OnEvent(Event& ev) + // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error + int PrepareIO(StreamSocket* sock) + { + if (status == ISSL_HANDSHAKEN) + return 1; + else if (status == ISSL_HANDSHAKING) + { + // The handshake isn't finished, try to finish it + return Handshake(sock); + } + + CloseSession(); + sock->SetError("No SSL session"); + return -1; + } + + static const char* UnknownIfNULL(const char* str) + { + return str ? str : "UNKNOWN"; + } + + static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size) + { + StreamSocket* sock = reinterpret_cast(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(sock->GetIOHook()); +#endif + + if (sock->GetEventMask() & FD_READ_WILL_BLOCK) + { +#ifdef _WIN32 + gnutls_transport_set_errno(session->sess, EAGAIN); +#else + errno = EAGAIN; +#endif + return -1; + } + + int rv = SocketEngine::Recv(sock, reinterpret_cast(buffer), size, 0); + +#ifdef _WIN32 + if (rv < 0) + { + /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError() + * and then set errno appropriately. + * The gnutls library may also have a different errno variable than us, see + * gnutls_transport_set_errno(3). + */ + gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno); + } +#endif + + if (rv < (int)size) + SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK); + return rv; + } + +#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH + static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt) + { + StreamSocket* sock = reinterpret_cast(transportptr); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(sock->GetIOHook()); +#endif + + if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK) + { +#ifdef _WIN32 + gnutls_transport_set_errno(session->sess, EAGAIN); +#else + errno = EAGAIN; +#endif + return -1; + } + + // Cast the giovec_t to iovec not to IOVector so the correct function is called on Windows + int ret = SocketEngine::WriteV(sock, reinterpret_cast(iov), iovcnt); +#ifdef _WIN32 + // See the function above for more info about the usage of gnutls_transport_set_errno() on Windows + if (ret < 0) + gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno); +#endif + + int size = 0; + for (int i = 0; i < iovcnt; i++) + size += iov[i].iov_len; + + if (ret < size) + SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK); + return ret; + } + +#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH + static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size) + { + StreamSocket* sock = reinterpret_cast(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast(sock->GetIOHook()); +#endif + + if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK) + { +#ifdef _WIN32 + gnutls_transport_set_errno(session->sess, EAGAIN); +#else + errno = EAGAIN; +#endif + return -1; + } + + int rv = SocketEngine::Send(sock, reinterpret_cast(buffer), size, 0); + +#ifdef _WIN32 + if (rv < 0) + { + /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError() + * and then set errno appropriately. + * The gnutls library may also have a different errno variable than us, see + * gnutls_transport_set_errno(3). + */ + gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno); + } +#endif + + if (rv < (int)size) + SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK); + return rv; + } +#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH + + public: + GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference& sslprofile) + : SSLIOHook(hookprov) + , sess(NULL) + , status(ISSL_NONE) + , profile(sslprofile) + { + gnutls_init(&sess, flags); + gnutls_transport_set_ptr(sess, reinterpret_cast(sock)); +#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH + gnutls_transport_set_vec_push_function(sess, VectorPush); +#else + gnutls_transport_set_push_function(sess, gnutls_push_wrapper); +#endif + gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper); + profile->SetupSession(sess); + + sock->AddIOHook(this); + Handshake(sock); + } + + void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE + { + CloseSession(); + } + + int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE + { + // Finish handshake if needed + int prepret = PrepareIO(user); + if (prepret <= 0) + return prepret; + + // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN. + { + GnuTLS::DataReader reader(sess); + int ret = reader.ret(); + if (ret > 0) + { + reader.appendto(recvq); + return 1; + } + else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + return 0; + } + else if (ret == 0) + { + user->SetError("Connection closed"); + CloseSession(); + return -1; + } + else + { + user->SetError(gnutls_strerror(ret)); + CloseSession(); + return -1; + } + } + } + + int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE + { + // Finish handshake if needed + int prepret = PrepareIO(user); + if (prepret <= 0) + return prepret; + + // Session is ready for transferring application data + int ret = 0; + + { + ret = gnutls_record_send(this->sess, sendq.data(), sendq.length()); + + if (ret == (int)sendq.length()) + { + SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE); + return 1; + } + else if (ret > 0) + { + sendq.erase(0, ret); + SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE); + return 0; + } + else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0) + { + SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE); + return 0; + } + else // (ret < 0) + { + user->SetError(gnutls_strerror(ret)); + CloseSession(); + return -1; + } + } + } + + void TellCiphersAndFingerprint(LocalUser* user) + { + if (sess) + { + std::string text = "*** You are connected using SSL cipher '"; + GetCiphersuite(text); + text += '\''; + if (!certificate->fingerprint.empty()) + text += " and your SSL certificate fingerprint is " + certificate->fingerprint; + + user->WriteNotice(text); + } + } + + void GetCiphersuite(std::string& out) const + { + out.append(UnknownIfNULL(gnutls_protocol_get_name(gnutls_protocol_get_version(sess)))).push_back('-'); + out.append(UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)))).push_back('-'); + out.append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).push_back('-'); + out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))); + } + + GnuTLS::Profile* GetProfile() { return profile; } ++ bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); } +}; + +int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st) +{ +#ifndef GNUTLS_NEW_CERT_CALLBACK_API + st->type = GNUTLS_CRT_X509; +#else + st->cert_type = GNUTLS_CRT_X509; + st->key_type = GNUTLS_PRIVKEY_X509; +#endif + StreamSocket* sock = reinterpret_cast(gnutls_transport_get_ptr(sess)); + GnuTLS::X509Credentials& cred = static_cast(sock->GetIOHook())->GetProfile()->GetX509Credentials(); + + st->ncerts = cred.certs.size(); + st->cert.x509 = cred.certs.raw(); + st->key.x509 = cred.key.get(); + st->deinit_all = 0; + + return 0; +} + +class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider +{ + reference profile; + + public: + GnuTLSIOHookProvider(Module* mod, reference& prof) + : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL) + , profile(prof) + { + ServerInstance->Modules->AddService(*this); + } + + ~GnuTLSIOHookProvider() + { + ServerInstance->Modules->DelService(*this); + } + + void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE { - if (starttls.enabled) - capHandler.HandleEvent(ev); + new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile); + } + + void OnConnect(StreamSocket* sock) CXX11_OVERRIDE + { + new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile); + } +}; + +class ModuleSSLGnuTLS : public Module +{ + typedef std::vector > ProfileList; + + // First member of the class, gets constructed first and destructed last + GnuTLS::Init libinit; + RandGen randhandler; + ProfileList profiles; + + void ReadProfiles() + { + // First, store all profiles in a new, temporary container. If no problems occur, swap the two + // containers; this way if something goes wrong we can go back and continue using the current profiles, + // avoiding unpleasant situations where no new SSL connections are possible. + ProfileList newprofiles; + + ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile"); + if (tags.first == tags.second) + { + // No tags found, create a profile named "gnutls" from settings in the block + const std::string defname = "gnutls"; + ConfigTag* tag = ServerInstance->Config->ConfValue(defname); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No tags found; using settings from the tag"); + + try + { + reference profile(GnuTLS::Profile::Create(defname, tag)); + newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); + } + catch (CoreException& ex) + { + throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason()); + } + } + + for (ConfigIter i = tags.first; i != tags.second; ++i) + { + ConfigTag* tag = i->second; + if (tag->getString("provider") != "gnutls") + continue; + + std::string name = tag->getString("name"); + if (name.empty()) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring tag without name at " + tag->getTagLocation()); + continue; + } + + reference profile; + try + { + profile = GnuTLS::Profile::Create(name, tag); + } + catch (CoreException& ex) + { + throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); + } + + newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); + } + + // New profiles are ok, begin using them + // Old profiles are deleted when their refcount drops to zero + profiles.swap(newprofiles); } - ModResult OnCheckReady(LocalUser* user) + public: + ModuleSSLGnuTLS() { - if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN)) - return MOD_RES_DENY; +#ifndef GNUTLS_HAS_RND + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif + } + + void init() CXX11_OVERRIDE + { + ReadProfiles(); + ServerInstance->GenRandom = &randhandler; + } + + void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE + { + if(param != "ssl") + return; + + try + { + ReadProfiles(); + } + catch (ModuleException& ex) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings."); + } + } + + ~ModuleSSLGnuTLS() + { + ServerInstance->GenRandom = &ServerInstance->HandleGenRandom; + } + + void OnCleanup(int target_type, void* item) CXX11_OVERRIDE + { + if(target_type == TYPE_USER) + { + LocalUser* user = IS_LOCAL(static_cast(item)); + + if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this) + { + // User is using SSL, they're a local user, and they're using one of *our* SSL ports. + // Potentially there could be multiple SSL modules loaded at once on different ports. + ServerInstance->Users->QuitUser(user, "SSL module unloading"); + } + } + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides SSL support for clients", VF_VENDOR); + } + + void OnUserConnect(LocalUser* user) CXX11_OVERRIDE + { + IOHook* hook = user->eh.GetIOHook(); + if (hook && hook->prov->creator == this) + static_cast(hook)->TellCiphersAndFingerprint(user); + } ++ ++ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE ++ { ++ if ((user->eh.GetIOHook()) && (user->eh.GetIOHook()->prov->creator == this)) ++ { ++ GnuTLSIOHook* iohook = static_cast(user->eh.GetIOHook()); ++ if (!iohook->IsHandshakeDone()) ++ return MOD_RES_DENY; ++ } ++ + return MOD_RES_PASSTHRU; + } }; MODULE_INIT(ModuleSSLGnuTLS) diff --cc src/modules/extra/m_ssl_openssl.cpp index 0fd4608be,b21091d3f..c8a035fac --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@@ -658,183 -700,150 +658,197 @@@ class OpenSSLIOHook : public SSLIOHoo } } } - return 0; } - bool Handshake(StreamSocket* user, issl_session* session) + void TellCiphersAndFingerprint(LocalUser* user) { - int ret; + if (sess) + { + std::string text = "*** You are connected using SSL cipher '"; + GetCiphersuite(text); + text += '\''; + const std::string& fingerprint = certificate->fingerprint; + if (!fingerprint.empty()) + text += " and your SSL certificate fingerprint is " + fingerprint; - ERR_clear_error(); - if (session->outbound) - ret = SSL_connect(session->sess); - else - ret = SSL_accept(session->sess); + user->WriteNotice(text); + } + } - if (ret < 0) + void GetCiphersuite(std::string& out) const + { + out.append(SSL_get_version(sess)).push_back('-'); + out.append(SSL_get_cipher(sess)); + } ++ ++ bool IsHandshakeDone() const { return (status == ISSL_OPEN); } +}; + +static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc) +{ +#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION + OpenSSLIOHook* hook = static_cast(SSL_get_ex_data(ssl, exdataindex)); + hook->SSLInfoCallback(where, rc); +#endif +} + +class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider +{ + reference profile; + + public: + OpenSSLIOHookProvider(Module* mod, reference& prof) + : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL) + , profile(prof) + { + ServerInstance->Modules->AddService(*this); + } + + ~OpenSSLIOHookProvider() + { + ServerInstance->Modules->DelService(*this); + } + + void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE + { + new OpenSSLIOHook(this, sock, profile->CreateServerSession(), profile); + } + + void OnConnect(StreamSocket* sock) CXX11_OVERRIDE + { + new OpenSSLIOHook(this, sock, profile->CreateClientSession(), profile); + } +}; + +class ModuleSSLOpenSSL : public Module +{ + typedef std::vector > ProfileList; + + ProfileList profiles; + + void ReadProfiles() + { + ProfileList newprofiles; + ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile"); + if (tags.first == tags.second) { - int err = SSL_get_error(session->sess, ret); + // Create a default profile named "openssl" + const std::string defname = "openssl"; + ConfigTag* tag = ServerInstance->Config->ConfValue(defname); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No tags found, using settings from the tag"); - if (err == SSL_ERROR_WANT_READ) + try { - ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); - session->status = ISSL_HANDSHAKING; - return true; + reference profile(new OpenSSL::Profile(defname, tag)); + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } - else if (err == SSL_ERROR_WANT_WRITE) + catch (OpenSSL::Exception& ex) { - ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); - session->status = ISSL_HANDSHAKING; - return true; + throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason()); } - else - { - CloseSession(session); - } - - return false; } - else if (ret > 0) + + for (ConfigIter i = tags.first; i != tags.second; ++i) { - // Handshake complete. - VerifyCertificate(session, user); + ConfigTag* tag = i->second; + if (tag->getString("provider") != "openssl") + continue; - session->status = ISSL_OPEN; + std::string name = tag->getString("name"); + if (name.empty()) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring tag without name at " + tag->getTagLocation()); + continue; + } - ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); + reference profile; + try + { + profile = new OpenSSL::Profile(name, tag); + } + catch (CoreException& ex) + { + throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); + } - return true; + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } - else if (ret == 0) - { - CloseSession(session); - } - return false; + + profiles.swap(newprofiles); } - void CloseSession(issl_session* session) + public: + ModuleSSLOpenSSL() { - if (session->sess) - { - SSL_shutdown(session->sess); - SSL_free(session->sess); - } - - session->sess = NULL; - session->status = ISSL_NONE; - session->cert = NULL; + // Initialize OpenSSL + SSL_library_init(); + SSL_load_error_strings(); } - void VerifyCertificate(issl_session* session, StreamSocket* user) + void init() CXX11_OVERRIDE { - if (!session->sess || !user) - return; - - X509* cert; - ssl_cert* certinfo = new ssl_cert; - session->cert = certinfo; - unsigned int n; - unsigned char md[EVP_MAX_MD_SIZE]; - const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5(); + // Register application specific data + char exdatastr[] = "inspircd"; + exdataindex = SSL_get_ex_new_index(0, exdatastr, NULL, NULL, NULL); + if (exdataindex < 0) + throw ModuleException("Failed to register application specific data"); - cert = SSL_get_peer_certificate((SSL*)session->sess); + ReadProfiles(); + } - if (!cert) - { - certinfo->error = "Could not get peer certificate: "+std::string(get_error()); + void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE + { + if (param != "ssl") return; - } - certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK); - - if (!SelfSigned) + try { - certinfo->unknownsigner = false; - certinfo->trusted = true; + ReadProfiles(); } - else + catch (ModuleException& ex) { - certinfo->unknownsigner = true; - certinfo->trusted = false; + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings."); } + } - char buf[512]; - X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); - certinfo->dn = buf; - // Make sure there are no chars in the string that we consider invalid - if (certinfo->dn.find_first_of("\r\n") != std::string::npos) - certinfo->dn.clear(); - - X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); - certinfo->issuer = buf; - if (certinfo->issuer.find_first_of("\r\n") != std::string::npos) - certinfo->issuer.clear(); + void OnUserConnect(LocalUser* user) CXX11_OVERRIDE + { + IOHook* hook = user->eh.GetIOHook(); + if (hook && hook->prov->creator == this) + static_cast(hook)->TellCiphersAndFingerprint(user); + } - if (!X509_digest(cert, digest, md, &n)) - { - certinfo->error = "Out of memory generating fingerprint"; - } - else + void OnCleanup(int target_type, void* item) CXX11_OVERRIDE + { + if (target_type == TYPE_USER) { - certinfo->fingerprint = irc::hex(md, n); + LocalUser* user = IS_LOCAL((User*)item); + + if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this) + { + // User is using SSL, they're a local user, and they're using one of *our* SSL ports. + // Potentially there could be multiple SSL modules loaded at once on different ports. + ServerInstance->Users->QuitUser(user, "SSL module unloading"); + } } + } - if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0)) ++ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE ++ { ++ if ((user->eh.GetIOHook()) && (user->eh.GetIOHook()->prov->creator == this)) + { - certinfo->error = "Not activated, or expired certificate"; ++ OpenSSLIOHook* iohook = static_cast(user->eh.GetIOHook()); ++ if (!iohook->IsHandshakeDone()) ++ return MOD_RES_DENY; + } + - X509_free(cert); ++ return MOD_RES_PASSTHRU; + } -}; + -static int error_callback(const char *str, size_t len, void *u) -{ - ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1)); - - // - // XXX: Remove this line, it causes valgrind warnings... - // - // MD_update(&m, buf, j); - // - // - // ... ONLY JOKING! :-) - // - - return 0; -} + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides SSL support for clients", VF_VENDOR); + } +}; MODULE_INIT(ModuleSSLOpenSSL) diff --cc src/modules/m_restrictmsg.cpp index e0887e587,2a9f1dc93..279775d48 --- a/src/modules/m_restrictmsg.cpp +++ b/src/modules/m_restrictmsg.cpp @@@ -33,8 -52,9 +33,9 @@@ class ModuleRestrictMsg : public Modul // message allowed if: // (1) the sender is opered // (2) the recipient is opered + // (3) the recipient is on a ulined server // anything else, blocked. - if (u->IsOper() || user->IsOper()) - if (IS_OPER(u) || IS_OPER(user) || (uline && ServerInstance->ULine(u->server))) ++ if (u->IsOper() || user->IsOper() || u->server->IsULine()) { return MOD_RES_PASSTHRU; }