diff options
author | Adam <Adam@anope.org> | 2010-10-01 21:01:49 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2010-10-01 21:01:49 -0400 |
commit | d44f7971b129aa7ba80999f16f17b8c7499686e1 (patch) | |
tree | a86d08c3e641ed6b499b53b3bbb74e2a7f5b0dfb /modules | |
parent | 70056dd4689eeab4f7a9b31a921e0d7e40d5ed0d (diff) |
Rewrote some of the socket code to allow m_ssl to be a service.
This allows modules (xmlrpc) to create and accept SSL connections.
Also fixed unloading m_mysql at certain times and made the threading
engine always work correctly on Windows.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/extra/db_mysql.cpp | 2 | ||||
-rw-r--r-- | modules/extra/m_mysql.cpp | 8 | ||||
-rw-r--r-- | modules/extra/m_ssl.cpp | 246 | ||||
-rw-r--r-- | modules/extra/ssl.h | 9 | ||||
-rw-r--r-- | modules/socketengines/m_socketengine_epoll.cpp | 8 | ||||
-rw-r--r-- | modules/socketengines/m_socketengine_select.cpp | 26 |
6 files changed, 223 insertions, 76 deletions
diff --git a/modules/extra/db_mysql.cpp b/modules/extra/db_mysql.cpp index b15ce4de4..274f653b2 100644 --- a/modules/extra/db_mysql.cpp +++ b/modules/extra/db_mysql.cpp @@ -352,7 +352,7 @@ class DBMySQL : public Module return SQL ? SQL->Escape(query) : query; } - DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator), interface(this), SQL(this, "mysql/main") + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator), interface(this), SQL("mysql/main") { me = this; diff --git a/modules/extra/m_mysql.cpp b/modules/extra/m_mysql.cpp index f641aa655..de855cead 100644 --- a/modules/extra/m_mysql.cpp +++ b/modules/extra/m_mysql.cpp @@ -186,6 +186,7 @@ class ModuleSQL : public Module DThread->SetExitState(); DThread->Wakeup(); DThread->Join(); + delete DThread; delete SQLPipe; } @@ -234,6 +235,7 @@ class ModuleSQL : public Module { MySQLService *ss = new MySQLService(this, connname, database, server, user, password, port); this->MySQLServices.insert(std::make_pair(connname, ss)); + ModuleManager::RegisterService(ss); Log(LOG_NORMAL, "mysql") << "MySQL: Sucessfully connected to server " << connname << " (" << server << ")"; } @@ -284,13 +286,13 @@ MySQLService::~MySQLService() for (unsigned i = me->QueryRequests.size(); i > 0; --i) { - QueryRequest &r = me->QueryRequests[i]; + QueryRequest &r = me->QueryRequests[i - 1]; if (r.service == this) { if (r.interface) r.interface->OnError(SQLResult("", "SQL Interface is going away")); - me->QueryRequests.erase(me->QueryRequests.begin() + i); + me->QueryRequests.erase(me->QueryRequests.begin() + i - 1); } } this->Lock.Unlock(); @@ -373,7 +375,7 @@ void DispatcherThread::Run() r.service->Lock.Unlock(); this->Lock(); - if (me->QueryRequests.front().query == r.query) + if (!me->QueryRequests.empty() && me->QueryRequests.front().query == r.query) { if (r.interface) me->FinishedRequests.push_back(QueryResult(r.interface, sresult)); diff --git a/modules/extra/m_ssl.cpp b/modules/extra/m_ssl.cpp index 5e081c193..9fefb942a 100644 --- a/modules/extra/m_ssl.cpp +++ b/modules/extra/m_ssl.cpp @@ -1,6 +1,7 @@ /* RequiredLibraries: ssl,crypt */ #include "module.h" +#include "ssl.h" #define OPENSSL_NO_SHA512 #include <openssl/bio.h> @@ -12,74 +13,98 @@ #define CERTFILE "anope.cert" #define KEYFILE "anope.key" -static SSL_CTX *ctx; +static SSL_CTX *server_ctx, *client_ctx; -class SSLSocket : public ClientSocket +class MySSLService : public SSLService { - private: - SSL *sslsock; - - int RecvInternal(char *buf, size_t sz) const - { - return SSL_read(sslsock, buf, sz); - } - - int SendInternal(const Anope::string &buf) const - { - return SSL_write(sslsock, buf.c_str(), buf.length()); - } public: - SSLSocket(const Anope::string &nTargetHost, int nPort, const Anope::string &nBindHost = "", bool nIPv6 = false) : ClientSocket(nTargetHost, nPort, nBindHost, nIPv6) - { - this->SetBlocking(); + MySSLService(Module *o, const Anope::string &n); - sslsock = SSL_new(ctx); + /** Initialize a socket to use SSL + * @param s The socket + */ + void Init(Socket *s); +}; - if (!sslsock) - throw CoreException("Unable to initialize SSL socket"); +class SSLSocketIO : public SocketIO +{ + public: + /* The SSL socket for this socket */ + SSL *sslsock; - SSL_set_connect_state(sslsock); - SSL_set_fd(sslsock, Sock); - SSL_connect(sslsock); + /** Constructor + */ + SSLSocketIO(); - UplinkSock = this; + /** Really 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 + */ + int Recv(Socket *s, char *buf, size_t sz) const; - this->SetNonBlocking(); - } + /** Really write something to the socket + * @param s The socket + * @param buf What to write + * @return Number of bytes written + */ + int Send(Socket *s, const Anope::string &buf) const; - ~SSLSocket() - { - SSL_shutdown(sslsock); - SSL_free(sslsock); + /** Accept a connection from a socket + * @param s The socket + */ + void Accept(ListenSocket *s); - UplinkSock = NULL; - } + /** Connect the socket + * @param s THe socket + * @param target IP to connect to + * @param port to connect to + * @param bindip IP to bind to, if any + */ + void Connect(ConnectionSocket *s, const Anope::string &target, int port, const Anope::string &bindip = ""); - bool Read(const Anope::string &buf) - { - process(buf); - return true; - } + /** Called when the socket is destructing + */ + void Destroy(); }; +class SSLModule; +static SSLModule *me; class SSLModule : public Module { + static int AlwaysAccept(int, X509_STORE_CTX *) + { + return 1; + } + public: - SSLModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator) + MySSLService service; + + SSLModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator), service(this, "ssl") { + me = this; + + this->SetAuthor("Anope"); + this->SetType(SUPPORTED); + this->SetPermanent(true); + + SSL_library_init(); SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); - ctx = SSL_CTX_new(SSLv23_client_method()); + client_ctx = SSL_CTX_new(SSLv23_client_method()); + server_ctx = SSL_CTX_new(SSLv23_server_method()); - if (!ctx) + if (!client_ctx || !server_ctx) throw ModuleException("Error initializing SSL CTX"); if (IsFile(CERTFILE)) { - if (!SSL_CTX_use_certificate_file(ctx, CERTFILE, SSL_FILETYPE_PEM)) + if (!SSL_CTX_use_certificate_file(client_ctx, CERTFILE, SSL_FILETYPE_PEM) || !SSL_CTX_use_certificate_file(server_ctx, CERTFILE, SSL_FILETYPE_PEM)) { - SSL_CTX_free(ctx); + SSL_CTX_free(client_ctx); + SSL_CTX_free(server_ctx); throw ModuleException("Error loading certificate"); } } @@ -88,9 +113,10 @@ class SSLModule : public Module if (IsFile(KEYFILE)) { - if (!SSL_CTX_use_PrivateKey_file(ctx, KEYFILE, SSL_FILETYPE_PEM)) + if (!SSL_CTX_use_PrivateKey_file(client_ctx, KEYFILE, SSL_FILETYPE_PEM) || !SSL_CTX_use_PrivateKey_file(server_ctx, KEYFILE, SSL_FILETYPE_PEM)) { - SSL_CTX_free(ctx); + SSL_CTX_free(client_ctx); + SSL_CTX_free(server_ctx); throw ModuleException("Error loading private key"); } } @@ -98,26 +124,29 @@ class SSLModule : public Module { if (IsFile(CERTFILE)) { - SSL_CTX_free(ctx); + SSL_CTX_free(client_ctx); + SSL_CTX_free(server_ctx); throw ModuleException("Error loading private key - file not found"); } else Log() << "m_ssl: No private key found"; } - this->SetAuthor("Anope"); - this->SetType(SUPPORTED); - this->SetPermanent(true); + SSL_CTX_set_mode(client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_mode(server_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_options(ctx, SSL_OP_TLS_ROLLBACK_BUG | SSL_OP_ALL); + SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, SSLModule::AlwaysAccept); + SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, SSLModule::AlwaysAccept); + + ModuleManager::RegisterService(&this->service); ModuleManager::Attach(I_OnPreServerConnect, this); } ~SSLModule() { - SSL_CTX_free(ctx); + SSL_CTX_free(client_ctx); + SSL_CTX_free(server_ctx); } EventReturn OnPreServerConnect(Uplink *u, int Number) @@ -128,19 +157,126 @@ class SSLModule : public Module { try { - new SSLSocket(u->host, u->port, Config->LocalHost, u->ipv6); - Log() << "Connected to Server " << Number << " (" << u->host << ":" << u->port << ")"; + new UplinkSocket(uplink_server->ipv6); + this->service.Init(UplinkSock); + UplinkSock->Connect(uplink_server->host, uplink_server->port, Config->LocalHost); + + Log() << "Connected to server " << Number << " (" << u->host << ":" << u->port << ") with SSL"; + return EVENT_ALLOW; } catch (const SocketException &ex) { - Log() << "Unable to connect with SSL to server" << Number << " (" << u->host << ":" << u->port << "), " << ex.GetReason(); + Log() << "Unable to connect with SSL to server " << Number << " (" << u->host << ":" << u->port << "), " << ex.GetReason(); } - return EVENT_ALLOW; + return EVENT_STOP; } return EVENT_CONTINUE; } }; +MySSLService::MySSLService(Module *o, const Anope::string &n) : SSLService(o, n) +{ +} + +void MySSLService::Init(Socket *s) +{ + if (s->IO != &normalSocketIO) + throw CoreException("Socket initializing SSL twice"); + + s->IO = new SSLSocketIO(); +} + +SSLSocketIO::SSLSocketIO() +{ + this->sslsock = NULL; +} + +int SSLSocketIO::Recv(Socket *s, char *buf, size_t sz) const +{ + size_t i = SSL_read(this->sslsock, buf, sz); + TotalRead += i; + return i; +} + +int SSLSocketIO::Send(Socket *s, const Anope::string &buf) const +{ + size_t i = SSL_write(this->sslsock, buf.c_str(), buf.length()); + TotalWritten += i; + return i; +} + +void SSLSocketIO::Accept(ListenSocket *s) +{ + sockaddrs conaddr; + + socklen_t size = conaddr.size(); + int newsock = accept(s->GetFD(), &conaddr.sa, &size); + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +#endif + if (newsock <= 0 || newsock == INVALID_SOCKET) + throw SocketException("Unable to accept SSL socket: " + Anope::LastError()); + + ClientSocket *newsocket = s->OnAccept(newsock, conaddr); + me->service.Init(newsocket); + SSLSocketIO *IO = debug_cast<SSLSocketIO *>(newsocket->IO); + + IO->sslsock = SSL_new(server_ctx); + if (!IO->sslsock) + throw SocketException("Unable to initialize SSL socket"); + + SSL_set_accept_state(IO->sslsock); + + if (!SSL_set_fd(IO->sslsock, newsock)) + throw SocketException("Unable to set SSL fd"); + + int ret = SSL_accept(IO->sslsock); + if (ret <= 0) + { + int error = SSL_get_error(IO->sslsock, ret); + + if (ret != -1 || (error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_READ)) + throw SocketException("Unable to accept new SSL connection: " + Anope::string(ERR_error_string(ERR_get_error(), NULL))); + } +} + +void SSLSocketIO::Connect(ConnectionSocket *s, const Anope::string &TargetHost, int Port, const Anope::string &BindHost) +{ + if (s->IO == &normalSocketIO) + throw SocketException("Attempting to connect uninitialized socket with SQL"); + + normalSocketIO.Connect(s, TargetHost, Port, BindHost); + + SSLSocketIO *IO = debug_cast<SSLSocketIO *>(s->IO); + + IO->sslsock = SSL_new(client_ctx); + if (!IO->sslsock) + throw SocketException("Unable to initialize SSL socket"); + + if (!SSL_set_fd(IO->sslsock, s->GetFD())) + throw SocketException("Unable to set SSL fd"); + + int ret = SSL_connect(IO->sslsock); + + if (ret <= 0) + { + int error = SSL_get_error(IO->sslsock, ret); + + if (ret != -1 || (error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_READ)) + throw SocketException("Unable to connect to server: " + Anope::string(ERR_error_string(ERR_get_error(), NULL))); + } +} + +void SSLSocketIO::Destroy() +{ + if (this->sslsock) + { + SSL_shutdown(this->sslsock); + SSL_free(this->sslsock); + } +} + MODULE_INIT(SSLModule) diff --git a/modules/extra/ssl.h b/modules/extra/ssl.h new file mode 100644 index 000000000..e25251379 --- /dev/null +++ b/modules/extra/ssl.h @@ -0,0 +1,9 @@ + +class SSLService : public Service +{ + public: + SSLService(Module *o, const Anope::string &n) : Service(o, n) { } + + virtual void Init(Socket *s) = 0; +}; + diff --git a/modules/socketengines/m_socketengine_epoll.cpp b/modules/socketengines/m_socketengine_epoll.cpp index 7b8dbdde5..713c1f0ff 100644 --- a/modules/socketengines/m_socketengine_epoll.cpp +++ b/modules/socketengines/m_socketengine_epoll.cpp @@ -48,7 +48,7 @@ class SocketEngineEPoll : public SocketEngineBase memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = s->GetSock(); + ev.data.fd = s->GetFD(); if (epoll_ctl(EngineHandle, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { @@ -67,7 +67,7 @@ class SocketEngineEPoll : public SocketEngineBase memset(&ev, 0, sizeof(ev)); - ev.data.fd = s->GetSock(); + ev.data.fd = s->GetFD(); if (epoll_ctl(EngineHandle, EPOLL_CTL_DEL, ev.data.fd, &ev) == -1) { @@ -90,7 +90,7 @@ class SocketEngineEPoll : public SocketEngineBase memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN | EPOLLOUT; - ev.data.fd = s->GetSock(); + ev.data.fd = s->GetFD(); if (epoll_ctl(EngineHandle, EPOLL_CTL_MOD, ev.data.fd, &ev) == -1) Log() << "Unable to mark fd " << ev.data.fd << " as writable in socketengine epoll: " << Anope::LastError(); @@ -108,7 +108,7 @@ class SocketEngineEPoll : public SocketEngineBase memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = s->GetSock(); + ev.data.fd = s->GetFD(); if (epoll_ctl(EngineHandle, EPOLL_CTL_MOD, ev.data.fd, &ev) == -1) Log() << "Unable to mark fd " << ev.data.fd << " as unwritable in socketengine epoll: " << Anope::LastError(); diff --git a/modules/socketengines/m_socketengine_select.cpp b/modules/socketengines/m_socketengine_select.cpp index 733547167..b1c1c065f 100644 --- a/modules/socketengines/m_socketengine_select.cpp +++ b/modules/socketengines/m_socketengine_select.cpp @@ -26,26 +26,26 @@ class SocketEngineSelect : public SocketEngineBase void AddSocket(Socket *s) { - if (s->GetSock() > MaxFD) - MaxFD = s->GetSock(); - FD_SET(s->GetSock(), &ReadFDs); - Sockets.insert(std::make_pair(s->GetSock(), s)); + if (s->GetFD() > MaxFD) + MaxFD = s->GetFD(); + FD_SET(s->GetFD(), &ReadFDs); + Sockets.insert(std::make_pair(s->GetFD(), s)); } void DelSocket(Socket *s) { - if (s->GetSock() == MaxFD) + if (s->GetFD() == MaxFD) --MaxFD; - FD_CLR(s->GetSock(), &ReadFDs); - FD_CLR(s->GetSock(), &WriteFDs); - Sockets.erase(s->GetSock()); + FD_CLR(s->GetFD(), &ReadFDs); + FD_CLR(s->GetFD(), &WriteFDs); + Sockets.erase(s->GetFD()); } void MarkWritable(Socket *s) { if (s->HasFlag(SF_WRITABLE)) return; - FD_SET(s->GetSock(), &WriteFDs); + FD_SET(s->GetFD(), &WriteFDs); s->SetFlag(SF_WRITABLE); } @@ -53,7 +53,7 @@ class SocketEngineSelect : public SocketEngineBase { if (!s->HasFlag(SF_WRITABLE)) return; - FD_CLR(s->GetSock(), &WriteFDs); + FD_CLR(s->GetFD(), &WriteFDs); s->UnsetFlag(SF_WRITABLE); } @@ -79,15 +79,15 @@ class SocketEngineSelect : public SocketEngineBase if (s->HasFlag(SF_DEAD)) continue; - if (FD_ISSET(s->GetSock(), &efdset)) + if (FD_ISSET(s->GetFD(), &efdset)) { s->ProcessError(); s->SetFlag(SF_DEAD); continue; } - if (FD_ISSET(s->GetSock(), &rfdset) && !s->ProcessRead()) + if (FD_ISSET(s->GetFD(), &rfdset) && !s->ProcessRead()) s->SetFlag(SF_DEAD); - if (FD_ISSET(s->GetSock(), &wfdset) && !s->ProcessWrite()) + if (FD_ISSET(s->GetFD(), &wfdset) && !s->ProcessWrite()) s->SetFlag(SF_DEAD); } |