]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_ssl_gnutls.cpp
Merge pull request #1044 from SaberUK/master+multichar-prefix
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_gnutls.cpp
index 0bc7060b8d6c2aa913ef3627aedbed828dd33cb7..d33403abade42dfef29d4f625f8dc9df1814b1f7 100644 (file)
@@ -73,6 +73,7 @@ typedef gnutls_dh_params_t gnutls_dh_params;
 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
 
 #if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
+#define INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 #define GNUTLS_NEW_CERT_CALLBACK_API
 typedef gnutls_retr2_st cert_cb_last_param_type;
 #else
@@ -833,6 +834,42 @@ info_done_dealloc:
                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<StreamSocket*>(transportptr);
+#ifdef _WIN32
+               GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(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<const iovec*>(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<StreamSocket*>(session_wrap);
@@ -868,17 +905,22 @@ info_done_dealloc:
                        SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
                return rv;
        }
+#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 
  public:
-       GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool outbound, const reference<GnuTLS::Profile>& sslprofile)
+       GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference<GnuTLS::Profile>& sslprofile)
                : SSLIOHook(hookprov)
                , sess(NULL)
                , status(ISSL_NONE)
                , profile(sslprofile)
        {
-               gnutls_init(&sess, outbound ? GNUTLS_SERVER : GNUTLS_CLIENT);
+               gnutls_init(&sess, flags);
                gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(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);
 
@@ -987,6 +1029,7 @@ info_done_dealloc:
        }
 
        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)
@@ -1027,12 +1070,12 @@ class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider
 
        void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
-               new GnuTLSIOHook(this, sock, true, profile);
+               new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile);
        }
 
        void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
        {
-               new GnuTLSIOHook(this, sock, false, profile);
+               new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
        }
 };
 
@@ -1162,6 +1205,18 @@ class ModuleSSLGnuTLS : public Module
                if (hook && hook->prov->creator == this)
                        static_cast<GnuTLSIOHook*>(hook)->TellCiphersAndFingerprint(user);
        }
+
+       ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+       {
+               if ((user->eh.GetIOHook()) && (user->eh.GetIOHook()->prov->creator == this))
+               {
+                       GnuTLSIOHook* iohook = static_cast<GnuTLSIOHook*>(user->eh.GetIOHook());
+                       if (!iohook->IsHandshakeDone())
+                               return MOD_RES_DENY;
+               }
+
+               return MOD_RES_PASSTHRU;
+       }
 };
 
 MODULE_INIT(ModuleSSLGnuTLS)