#include #include #include #include #include #include // TODO: Fix this shit! #include "../../../admins.h" #include "common.h" #include "proto.h" #include "config_thread.h" #include "root_parser.h" void DumpCrypto(const char * data, size_t size) { std::string dumpstr = ""; for (unsigned i = 0; i < size; ++i) { std::string ch; strprintf(&ch, "%x", *(data + i)); dumpstr += ch; } printfd(__FILE__, "Crypto dump: '%s'\n", dumpstr.c_str()); } CONFIG_THREAD::CONFIG_THREAD(ADMINS * a, TARIFFS * t, USERS * u, const SETTINGS * s) : sd(-1), done(false), state(ST_NOOP), respCode(RESP::OK), admins(a), tariffs(t), users(u), settings(s) { /*printfd(__FILE__, "sizeof(REQ::HEADER) = %d\n", sizeof(REQ::HEADER)); printfd(__FILE__, "sizeof(REQ::CRYPTO_HEADER) = %d\n", sizeof(REQ::CRYPTO_HEADER)); printfd(__FILE__, "sizeof(RESP::HEADER) = %d\n", sizeof(RESP::HEADER)); printfd(__FILE__, "sizeof(RESP::CRYPTO_HEADER) = %d\n", sizeof(RESP::CRYPTO_HEADER));*/ assert(sizeof(REQ::HEADER) % 8 == 0); assert(sizeof(REQ::CRYPTO_HEADER) % 8 == 0); assert(sizeof(RESP::HEADER) % 8 == 0); assert(sizeof(RESP::CRYPTO_HEADER) % 8 == 0); iv = new unsigned char[8]; memset(iv, 0, 8); } CONFIG_THREAD::CONFIG_THREAD(const CONFIG_THREAD & rvalue) : sd(rvalue.sd), remoteAddr(rvalue.remoteAddr), done(false), state(ST_NOOP), respCode(rvalue.respCode), admins(rvalue.admins), tariffs(rvalue.tariffs), users(rvalue.users), settings(rvalue.settings) { assert(!rvalue.done); iv = new unsigned char[8]; memcpy(iv, rvalue.iv, 8); } CONFIG_THREAD & CONFIG_THREAD::operator=(const CONFIG_THREAD & rvalue) { assert(0 && "Never be here"); return *this; } CONFIG_THREAD::~CONFIG_THREAD() { //assert(done); delete[] iv; } void CONFIG_THREAD::operator() () { if (sd < 0) { printfd(__FILE__, "CONFIG_THREAD::operator()() Invalid socket descriptor\n"); return; } if (ReadReq()) { Process(); } WriteResp(); close(sd); { boost::mutex::scoped_lock lock(mutex); done = true; } } bool CONFIG_THREAD::IsDone() const { boost::mutex::scoped_lock lock(mutex); return done; } void CONFIG_THREAD::SetConnection(int sock, struct sockaddr_in sin) { sd = sock; remoteAddr = sin; } bool CONFIG_THREAD::ReadBlock(void * dest, size_t & size, int timeout) const { unsigned readSize = 0; char * ptr = static_cast(dest); while (readSize < size) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeout * 1000; fd_set rfds; FD_ZERO(&rfds); FD_SET(sd, &rfds); int res = select(sd + 1, &rfds, NULL, NULL, &tv); /* Don't rely on the value of tv now! */ if (res < 0) { printfd(__FILE__, "CONFIG_THREAD::ReadBlock() Select error: '%s'\n", strerror(errno)); return false; } if (res == 0) { // Timeout size = readSize; return false; } res = read(sd, ptr + readSize, size - readSize); if (res == 0) { // EOF printfd(__FILE__, "CONFIG_THREAD::ReadBlock() EOF\n"); return false; } // Ignore 'Interrupted system call' errors if (res < 0) { if (errno != EINTR) { printfd(__FILE__, "CONFIG_THREAD::ReadBlock() Read error: '%s'\n", strerror(errno)); return false; } else { continue; } } readSize += res; } return true; } bool CONFIG_THREAD::WriteBlock(const void * source, size_t & size, int timeout) const { const char * ptr = static_cast(source); unsigned writeSize = 0; while (writeSize < size) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeout * 1000; fd_set wfds; FD_ZERO(&wfds); FD_SET(sd, &wfds); int res = select(sd + 1, NULL, &wfds, NULL, &tv); /* Don't rely on the value of tv now! */ if (res < 0) { printfd(__FILE__, "CONFIG_THREAD::WriteBlock() Select error: '%s'\n", strerror(errno)); return false; } if (res == 0) { // Timeout size = writeSize; return false; } res = write(sd, ptr + writeSize, size - writeSize); // Ignore 'Interrupted system call' errors if (res < 0 && errno != EINTR) { printfd(__FILE__, "CONFIG_THREAD::WriteBlock() Write error: '%s'\n", strerror(errno)); return false; } writeSize += res; } return true; } bool CONFIG_THREAD::ReadReq() { struct REQ::HEADER reqHeader; size_t size = sizeof(reqHeader); if (!ReadBlock(&reqHeader, size, 5000)) { state = ST_ERROR; message = "No request header within 5 sec"; printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str()); return false; } if (strncmp(reqHeader.magic, PROTO_MAGIC, sizeof(reqHeader.magic))) { state = ST_ERROR; respCode = RESP::INVALID_MAGIC; message = "Invalid magic code in header"; printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str()); return false; } uint32_t version = ntohl(reqHeader.version); if (version > (2 << 8 | 0)) { state = ST_ERROR; respCode = RESP::UNSUPPORTED_VERSION; message = "Unsupported version"; printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s (wanted: %d, actual: %d)\n", message.c_str(), (2 << 8 | 0), version); return false; } versionMinor = version & 0x0000FFFF; versionMajor = (version >> 8) & 0x0000FFFF; reqHeader.login[sizeof(reqHeader.login) - 1] = 0; login = reqHeader.login; if (!CheckLogin(login, password)) { state = ST_ERROR; respCode = RESP::INVALID_CREDENTIALS; message = "Unknown login"; printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str()); return false; } return ReceiveData(); } bool CONFIG_THREAD::ReceiveData() { unsigned char buffer[sizeof(struct REQ::CRYPTO_HEADER)]; //unsigned char iv[] = "00000000"; size_t size = sizeof(struct REQ::CRYPTO_HEADER); if (!ReadBlock(buffer, size, 5000)) { state = ST_ERROR; message = "No crypto header within 5 secs"; printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str()); return false; } BF_set_key(&key, password.length(), reinterpret_cast(password.c_str())); struct REQ::CRYPTO_HEADER reqCryptoHeader; BF_cbc_encrypt(buffer, reinterpret_cast(&reqCryptoHeader), sizeof(struct REQ::CRYPTO_HEADER), &key, iv, BF_DECRYPT); reqCryptoHeader.login[sizeof(reqCryptoHeader.login) - 1] = 0; std::string cryptoLogin(reqCryptoHeader.login); if (login != cryptoLogin) { state = ST_ERROR; respCode = RESP::INVALID_CREDENTIALS; message = "Password is invalid"; printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str()); return false; } //assert(reqCryptoHeader.dataSize % 8 == 0); char block[1496]; unsigned char cryptoBlock[1496]; size_t length = 0; uint32_t dataSize = ntohl(reqCryptoHeader.dataSize); while (length < dataSize) { size_t delta = dataSize - length; if (delta > sizeof(cryptoBlock)) { delta = sizeof(cryptoBlock); } size_t bs = delta; ReadBlock(cryptoBlock, bs, 5000); if (bs != delta) { state = ST_ERROR; message = "No data within 5 secs"; printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str()); return false; } BF_cbc_encrypt(cryptoBlock, reinterpret_cast(block), bs, &key, iv, BF_DECRYPT); xml.append(block, bs); length += bs; } return true; } void CONFIG_THREAD::Process() { ROOT_PARSER parser(currAdmin, tariffs, users, settings); XML_Parser p; p= XML_ParserCreate(NULL); XML_SetElementHandler(p, &TagBegin, &TagEnd); XML_SetUserData(p, &parser); if (!XML_Parse(p, xml.c_str(), xml.length(), true)) { printfd(__FILE__, "CONFIG_THREAD::Process() Error: '%s' at line %d\n", XML_ErrorString(XML_GetErrorCode(p)), XML_GetCurrentLineNumber(p)); //MakeErrorXML(); } XML_ParserFree(p); xml = parser.GetResult(); } void CONFIG_THREAD::WriteResp() const { RESP::HEADER respHeader; strncpy(respHeader.magic, PROTO_MAGIC, sizeof(respHeader.magic)); respHeader.version = htonl(2 << 8 | 0); respHeader.code = respCode; RESP::CRYPTO_HEADER respCryptoHeader; strncpy(respCryptoHeader.login, login.c_str(), sizeof(respCryptoHeader.login)); if (xml.size() % 8 == 0) { respCryptoHeader.dataSize = htonl(xml.size()); } else { respCryptoHeader.dataSize = htonl((xml.size() / 8 + 1) * 8); } size_t size = sizeof(respHeader); if (!WriteBlock(&respHeader, size, 5000)) { printfd(__FILE__, "CONFIG_THREAD::WriteResp() Failed to send answer header\n"); return; } if (state != ST_ERROR) { unsigned char buffer[sizeof(respCryptoHeader)]; size = sizeof(respCryptoHeader); BF_cbc_encrypt(reinterpret_cast(&respCryptoHeader), buffer, size, &key, iv, BF_ENCRYPT); if (!WriteBlock(buffer, size, 5000)) { printfd(__FILE__, "CONFIG_THREAD::WriteResp() Failed to send answer crypto-header\n"); return; } SendData(); } } void CONFIG_THREAD::SendData() const { size_t pos = 0; std::string data(xml); if (data.size() % 8) { size_t delta = (data.size() / 8 + 1) * 8 - data.size(); data.append(delta, ' '); } while (pos < data.size()) { unsigned char source[1496]; unsigned char buffer[1496]; size_t size; if (data.size() - pos > sizeof(source)) { memcpy(source, data.c_str() + pos, sizeof(source)); size = sizeof(source); } else { memset(source, 0, sizeof(source)); memcpy(source, data.c_str() + pos, data.size() - pos); size = data.size() - pos; } BF_cbc_encrypt(source, buffer, size, &key, iv, BF_ENCRYPT); if (!WriteBlock(buffer, size, 5000)) { printfd(__FILE__, "CONFIG_THREAD::SendData() Failed to write data block\n"); return; } pos += size; // size? } return; } bool CONFIG_THREAD::CheckLogin(const std::string & login, std::string & password) { currAdmin = admins->FindAdmin(login); if (currAdmin == NULL) { printfd(__FILE__, "CONFIG_THREAD::CheckLogin() Admin '%s' not found\n", login.c_str()); return false; } password = currAdmin->GetPassword(); return true; } void CONFIG_THREAD::TagBegin(void * userData, const char * name, const char ** attr) { ROOT_PARSER * self = static_cast(userData); self->StartTag(name, attr); } void CONFIG_THREAD::TagEnd(void * userData, const char * name) { ROOT_PARSER * self = static_cast(userData); self->EndTag(name); }