/* * Copyright 2016 Bjango Pty Ltd. All rights reserved. * Copyright 2010 William Tisäter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The name of the copyright holder may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ALLOCA_H # include #endif #include #include #ifdef HAVE_LIBZLIB #include #endif #include "Utility.h" #include "Socket.h" using namespace std; #ifdef HAVE_LIBZLIB std::string compress_string(const std::string& str, int compressionlevel = Z_BEST_COMPRESSION) { z_stream zs; // z_stream is zlib's control structure memset(&zs, 0, sizeof(zs)); if (deflateInit(&zs, compressionlevel) != Z_OK) { return ""; } zs.next_in = (Bytef*)str.data(); zs.avail_in = str.size(); // set the z_stream's input int ret; char outbuffer[32768]; std::string outstring; // retrieve the compressed bytes blockwise do { zs.next_out = reinterpret_cast(outbuffer); zs.avail_out = sizeof(outbuffer); ret = deflate(&zs, Z_FINISH); if (outstring.size() < zs.total_out) { // append the block to the output string outstring.append(outbuffer, zs.total_out - outstring.size()); } } while (ret == Z_OK); deflateEnd(&zs); if (ret != Z_STREAM_END) { // an error occurred that was not EOF return ""; } return outstring; } #endif int Socket::listen() { int yes = 1; hostent * host = NULL; if ((socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) { cout << "Could not create socket: " << strerror(errno) << endl; return 0; } setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &yes, sizeof(yes)); struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; if (setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) { cout << "setsockopt failed" << endl; } if (setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0){ cout << "setsockopt failed" << endl; } sockaddr_in myAddress; memset(&myAddress, 0, sizeof(sockaddr_in)); if (!(host = gethostbyname(address.c_str()))) myAddress.sin_addr.s_addr = inet_addr(address.c_str()); else myAddress.sin_addr.s_addr = ((in_addr *) host->h_addr)->s_addr; myAddress.sin_family = AF_INET; myAddress.sin_port = htons(port); if (::bind(socket, (sockaddr *) &myAddress, sizeof(myAddress)) == -1) { cout << "Could not bind socket: " << strerror(errno) << endl; return 0; } // cout << "(" << address << ":" << port << ") Socket binded." << endl; if (::listen(socket, 5) == -1) { cout << "Could not listen on socket: " << strerror(errno) << endl; return 0; } // cout << "(" << address << ":" << port << ") Socket initialized." << endl; return 1; } Socket Socket::accept() { int theirSocket; sockaddr_in theirAddress; socklen_t size = sizeof(sockaddr_in); if ((theirSocket = ::accept(socket, (sockaddr *) &theirAddress, &size)) == -1) { cout << "Could not accept connection: " << strerror(errno) << endl; } Socket socket(theirSocket, inet_ntoa(theirAddress.sin_addr), ntohs(theirAddress.sin_port)); socket.lastRequest = get_current_time(); socket.debugLogging = false; socket.readbuf = ""; socket._session = _session; socket._serverUUID = _serverUUID; socket._sslEnabled = _sslEnabled; socket.sslContext = sslContext; cout << socket.get_description() << " New connection accepted. " << endl; fcntl(socket.get_id(), F_SETFL, fcntl(socket.get_id(), F_GETFL, 0) | O_NONBLOCK); /* struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; if (setsockopt (theirSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) cout << "setsockopt failed" << endl; if (setsockopt (theirSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) cout << "setsockopt failed" << endl; */ return socket; } string Socket::get_description() { stringstream desc; desc << get_current_time_string() << " - (" << get_address() << ":" << get_port(); if(_name.length() > 0) desc << " - " << _name; desc << ")"; return desc.str(); } int Socket::send(string data) { // cout << "Writing- " << data << endl; int r = 0; if(secure == true) { while(data.size() > 0) { ERR_clear_error(); unsigned int limit = 8192; if(data.size() > limit) { if(debugLogging) cout << get_description() << " Writing " << limit << " bytes" << endl; r = SSL_write(ssl, data.substr(0, limit).c_str(), limit); } else { if(debugLogging) cout << get_description() << " Writing " << data.size() << " bytes" << endl; r = SSL_write(ssl, data.c_str(), data.size()); } if(debugLogging) cout << get_description() << " Write result " << r << ", ssl error " << SSL_get_error (ssl,r) << ", errorno " << errno << ", err_get_error " << ERR_get_error() << endl; switch ( SSL_get_error (ssl,r) ){ case SSL_ERROR_NONE: data = data.substr(r, readbuf.size() - r); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_ZERO_RETURN: cout << get_description() << " SSL connection closed by peer" << endl; return 0; break; default: // cout << get_description() << " SSL error - " << ERR_error_string( SSL_get_error(ssl, r), NULL ) << endl; cout << get_description() << " SSL write error - " << ERR_error_string( ERR_get_error(), NULL ) << endl; return 0; break; } } } else { if ((r = ::send(socket, data.c_str(), data.size(), 0)) == -1) { cout << get_description() << " Could not send data: " << strerror(errno) << endl; return 0; } } // cout << "(" << address << ":" << port << ") Data sent: " << _data << endl; return r; } int Socket::receive(ClientSet * _clients, Config * _config, Stats * _stats) { char buf[1024]; int len = 0; if(secure == true) { do { memset(buf, 0, 1024); ERR_clear_error(); int r = SSL_read (ssl, buf, 1024); if(debugLogging) cout << get_description() << " Read result " << r << ", ssl error " << SSL_get_error (ssl,r) << ", errorno " << errno << ", err_get_error " << ERR_get_error() << endl; switch ( SSL_get_error (ssl,r) ){ case SSL_ERROR_NONE: len += r; readbuf += buf; // if(debugLogging) // cout << "Read bytes " << buf << endl; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_ZERO_RETURN: cout << get_description() << " SSL connection closed by peer" << endl; return 0; break; default: // cout << get_description() << " SSL read error - " << ERR_error_string( SSL_get_error(ssl, r), NULL ) << endl; cout << get_description() << " SSL read error - " << ERR_error_string( ERR_get_error(), NULL ) << endl; return 0; break; } } while(SSL_pending (ssl) > 0); } else { while(1) { memset(buf, 0, 1024); #ifdef MSG_DONTWAIT int ret = recv(socket, buf, 1024, MSG_DONTWAIT); #else int ret = recv(socket, buf, 1024, MSG_NONBLOCK); #endif if(ret < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { break; } else if(ret <= 0) { return 0; } else { len += ret; readbuf += buf; } } } size_t position = readbuf.find(""); if(position != std::string::npos) { lastRequest = get_current_time(); string xml = readbuf.substr(0, position); // if(debugLogging) // cout << get_description() << " Read xml data " << xml << endl; parse(readbuf, _clients, _config, _stats); position += 6; readbuf = readbuf.substr(position, readbuf.size() - position); } return len; } void Socket::close() { } int Socket::startSSL() { ssl = SSL_new(sslContext); SSL_set_fd(ssl, socket); SSL_set_accept_state(ssl); int code = SSL_accept(ssl); if ( code != 1){ int error = SSL_get_error(ssl, code); while (code != 1 && (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)) { code = SSL_accept(ssl); error = SSL_get_error(ssl, code); } if(code != 1) { cout << get_description() << " Error starting SSL: " << ERR_error_string( SSL_get_error(ssl, code), NULL ) <get("server_code", "00000"); string cf_server_reject_delay = _config->get("server_reject_delay", "3"); xmlDocPtr doc; xmlNodePtr cur; xmlParserCtxtPtr ctxt = xmlNewParserCtxt(); doc = xmlCtxtReadMemory(ctxt, _data.c_str(), _data.length(), NULL, NULL, 0); if ((cur = xmlDocGetRootElement(doc)) != NULL) { if (xmlStrEqual(cur->name, BAD_CAST "isr")) { char *type = (char *)xmlGetProp(cur, (const xmlChar *)"type"); int code = atoi(type); free(type); if(code == 100){ char *protocol = (char *)xmlGetProp(cur, (const xmlChar *)"protocol"); _protocol = atoi(protocol); send(isr_accept_connection()); free(protocol); } if(code == 101){ char *uuid = (char *)xmlGetProp(cur, (const xmlChar *)"uuid"); char *name = (char *)xmlGetProp(cur, (const xmlChar *)"name"); char *version = (char *)xmlGetProp(cur, (const xmlChar *)"version"); _uuid = string(uuid); _name = string(name); int auth = 1; if (_clients->is_authenticated(_uuid)) auth = 0; else { int code = atoi(cf_server_code.c_str()); std::ostringstream stm; stm << code; string stringcode = stm.str(); // check if code is a 5 digit number if(cf_server_code != stringcode || cf_server_code.length() != 5){ auth = 2; // text based passcode } } send(isr_serverinfo(_session, auth, _serverUUID, _stats->historyEnabled)); free(uuid); free(name); free(version); } if(code == 102){ char *code = (char *)xmlGetProp(cur, (const xmlChar *)"code"); if (code == cf_server_code) { _clients->authenticate(_uuid); send(isr_accept_code()); } else { send(isr_reject_code()); } free(code); } if(code == 103){ pthread_mutex_lock(&_stats->lock); temp << isr_create_header() << ""; if (_clients->is_authenticated(_uuid)) { xmlNodePtr child = cur->children; while (child){ char *type = (char *)xmlGetProp(child, (const xmlChar *)"type"); if(type == NULL) { child = child->next; continue; } if(strcmp(type, "cpu") == 0) { temp << isr_cpu_data(child, _stats); } if(strcmp(type, "memory") == 0) { temp << isr_memory_data(child, _stats); } if(strcmp(type, "load") == 0) { temp << isr_loadavg_data(child, _stats); } if(strcmp(type, "network") == 0) { #ifndef USE_NET_NONE temp << isr_multiple_data(child, _stats); #endif } if(strcmp(type, "diskactivity") == 0) { #ifndef USE_ACTIVITY_NONE temp << isr_multiple_data(child, _stats); #endif } if(strcmp(type, "processes") == 0) { #ifndef USE_PROCESSES_NONE temp << isr_multiple_data(child, _stats); #endif } if(strcmp(type, "disks") == 0) { #ifndef USE_DISK_NONE temp << isr_multiple_data(child, _stats); #endif } if(strcmp(type, "sensors") == 0) { temp << isr_multiple_data(child, _stats); } if(strcmp(type, "uptime") == 0) { temp << isr_uptime_data(_stats->uptime()); } if(strcmp(type, "battery") == 0) { #ifndef USE_BATTERY_NONE temp << isr_multiple_data(child, _stats); #endif } free(type); child = child->next; } } pthread_mutex_unlock(&_stats->lock); temp << ""; string data = temp.str(); string compressedTag = ""; #ifdef HAVE_LIBZLIB string compressedData = compress_string(data, Z_BEST_COMPRESSION); if(compressedData.size() > 0) { compressedTag = " c=\"1\""; data = compressedData; } #endif stringstream temp2; temp2 << ""; send(temp2.str()); send(data); } } // Unknown element recived after header } // Failed to read header xmlFreeDoc(doc); xmlFreeParserCtxt(ctxt); }