diff options
Diffstat (limited to 'bouncer/server.cpp')
-rw-r--r-- | bouncer/server.cpp | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/bouncer/server.cpp b/bouncer/server.cpp new file mode 100644 index 0000000..16c754b --- /dev/null +++ b/bouncer/server.cpp @@ -0,0 +1,160 @@ +#include "core.h" +#include "dns.h" + +Server::Server(NetCore *_netCore) : SocketRWCommon(_netCore) { + dnsQueryId = -1; +} +Server::~Server() { + if (dnsQueryId != -1) + DNS::closeQuery(dnsQueryId); + close(); +} + + + +void Server::processReadBuffer() { + // Try to process as many lines as we can + char *buf = inputBuf.data(); + int bufSize = inputBuf.size(); + int lineBegin = 0, pos = 0; + + while (pos < bufSize) { + if (buf[pos] == '\r' || buf[pos] == '\n') { + if (pos > lineBegin) { + buf[pos] = 0; + lineReceivedEvent(&buf[lineBegin], pos - lineBegin); + } + + lineBegin = pos + 1; + } + + pos++; + } + + // If we managed to handle anything, lop it off the buffer + inputBuf.trimFromStart(lineBegin); +} + +void Server::sendLine(const char *line) { + outputBuf.append(line, strlen(line)); + outputBuf.append("\r\n", 2); +} + + +void Server::connect(const char *hostname, int _port, bool _useTls) { + if (state == CS_DISCONNECTED) { + port = _port; + useTls = _useTls; + + DNS::closeQuery(dnsQueryId); // just in case + dnsQueryId = DNS::makeQuery(hostname); + + if (dnsQueryId == -1) { + // TODO: better error reporting + printf("DNS query failed!\n"); + } else { + state = CS_WAITING_DNS; + } + } +} + +void Server::tryConnectPhase() { + if (state == CS_WAITING_DNS) { + in_addr result; + bool isError; + + if (DNS::checkQuery(dnsQueryId, &result, &isError)) { + DNS::closeQuery(dnsQueryId); + dnsQueryId = -1; + + if (isError) { + printf("DNS query failed at phase 2!\n"); + state = CS_DISCONNECTED; + } else { + // OK, if there was no error, we can go ahead and do this... + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + perror("[Server] Failed to socket()"); + close(); + return; + } + + if (!setSocketNonBlocking(sock)) { + perror("[Server] Could not set non-blocking"); + close(); + return; + } + + // We have our non-blocking socket, let's try connecting! + sockaddr_in outAddr; + outAddr.sin_family = AF_INET; + outAddr.sin_port = htons(port); + outAddr.sin_addr.s_addr = result.s_addr; + + if (::connect(sock, (sockaddr *)&outAddr, sizeof(outAddr)) == -1) { + if (errno == EINPROGRESS) { + state = CS_WAITING_CONNECT; + } else { + perror("[Server] Could not connect"); + close(); + } + } else { + // Whoa, we're connected? Neat. + connectionSuccessful(); + } + } + } + } +} + +void Server::connectionSuccessful() { + state = CS_CONNECTED; + + inputBuf.clear(); + outputBuf.clear(); + + // Do we need to do any TLS junk? +#ifdef USE_GNUTLS + if (useTls) { + state = CS_TLS_HANDSHAKE; + + int initRet = gnutls_init(&tls, GNUTLS_CLIENT); + if (initRet != GNUTLS_E_SUCCESS) { + printf("[Server::connectionSuccessful] gnutls_init borked\n"); + gnutls_perror(initRet); + close(); + return; + } + + // TODO: error check this + const char *errPos; + gnutls_priority_set_direct(tls, "NORMAL", &errPos); + + gnutls_credentials_set(tls, GNUTLS_CRD_CERTIFICATE, g_serverCreds); + + gnutls_transport_set_int(tls, sock); + + tlsActive = true; + } else +#endif + { + connectedEvent(); + } +} + +void Server::close() { + int saveState = state; + + SocketRWCommon::close(); + + if (dnsQueryId != -1) { + DNS::closeQuery(dnsQueryId); + dnsQueryId = -1; + } + + if (saveState == CS_CONNECTED) + disconnectedEvent(); +} + + |