/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Author : Boris Mikhailenko */ #include "configproto.h" #include "parser.h" #include "stg/admins.h" #include "stg/logger.h" #include "stg/common.h" #include "stg/blowfish.h" #include #include #include // strerror #include // close #include #include #include #include #ifndef ENODATA // FreeBSD 4.* - suxx #define ENODATA -1 #endif enum CONF_STATE { confHdr, confLogin, confLoginCipher, confData }; enum { ans_ok = 0, ans_err }; //----------------------------------------------------------------------------- int CONFIGPROTO::Prepare() { sigset_t sigmask, oldmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); sigaddset(&sigmask, SIGTERM); sigaddset(&sigmask, SIGUSR1); sigaddset(&sigmask, SIGHUP); pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask); listenSocket = socket(PF_INET, SOCK_STREAM, 0); if (listenSocket < 0) { errorStr = "Create socket failed."; logger("Cannot create a socket: %s", strerror(errno)); return -1; } struct sockaddr_in listenAddr; listenAddr.sin_family = PF_INET; listenAddr.sin_port = htons(port); listenAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); int lng = 1; if (0 != setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &lng, 4)) { errorStr = "Setsockopt failed. " + std::string(strerror(errno)); logger("setsockopt error: %s", strerror(errno)); return -1; } if (bind(listenSocket, (struct sockaddr*)&listenAddr, sizeof(listenAddr)) == -1) { errorStr = "Bind admin socket failed"; logger("Cannot bind the socket: %s", strerror(errno)); return -1; } if (listen(listenSocket, 0) == -1) { errorStr = "Listen admin socket failed"; logger("Cannot listen the socket: %s", strerror(errno)); return -1; } errorStr = ""; nonstop = true; return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::Stop() { nonstop = false; shutdown(listenSocket, SHUT_RDWR); close(listenSocket); //TODO: Idiotism struct sockaddr_in addr; addr.sin_family = PF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); socklen_t addrLen = sizeof(addr); int sock = socket(PF_INET, SOCK_STREAM, 0); connect(sock, (sockaddr*)&addr, addrLen); shutdown(sock, SHUT_RDWR); close(sock); //Idiotism end return 0; } //----------------------------------------------------------------------------- void CONFIGPROTO::Run() { while (nonstop) { state = confHdr; struct sockaddr_in outerAddr; socklen_t outerAddrLen(sizeof(outerAddr)); int outerSocket = accept(listenSocket, (struct sockaddr*)(&outerAddr), &outerAddrLen); if (!nonstop) break; if (outerSocket < 0) { logger("accept error: %s", strerror(errno)); printfd(__FILE__, "accept failed\n"); continue; } adminIP = *(unsigned int*)&(outerAddr.sin_addr); if (state == confHdr) { if (RecvHdr(outerSocket) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (state == confLogin) { if (SendHdrAnswer(outerSocket, ans_ok) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (RecvLogin(outerSocket) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (state == confLoginCipher) { if (SendLoginAnswer(outerSocket) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (RecvLoginS(outerSocket) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (state == confData) { if (SendLoginSAnswer(outerSocket, ans_ok) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } if (RecvData(outerSocket) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } state = confHdr; } else { if (SendLoginSAnswer(outerSocket, ans_err) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } WriteLogAccessFailed(adminIP); } } else { WriteLogAccessFailed(adminIP); } } else { WriteLogAccessFailed(adminIP); if (SendHdrAnswer(outerSocket, ans_err) < 0) { shutdown(outerSocket, SHUT_RDWR); close(outerSocket); continue; } } } else { WriteLogAccessFailed(adminIP); } printfd(__FILE__, "Successfull connection from %s\n", inet_ntostring(outerAddr.sin_addr.s_addr).c_str()); shutdown(outerSocket, SHUT_RDWR); close(outerSocket); } } //----------------------------------------------------------------------------- int CONFIGPROTO::RecvHdr(int sock) { char buf[sizeof(STG_HEADER)]; memset(buf, 0, sizeof(STG_HEADER)); size_t stgHdrLen = sizeof(STG_HEADER) - 1; // Without 0-char size_t pos = 0; while (pos < stgHdrLen) { if (!WaitPackets(sock)) { state = confHdr; SendError(sock, "Bad request"); return -1; } ssize_t ret = recv(sock, &buf[pos], static_cast(stgHdrLen) - static_cast(pos), 0); if (ret <= 0) { if (ret < 0) logger("recv error: %s", strerror(errno)); state = confHdr; return -1; } pos += ret; } if (0 == strncmp(buf, STG_HEADER, strlen(STG_HEADER))) { state = confLogin; return 0; } else { SendError(sock, "Bad request"); } state = confHdr; return -1; } //----------------------------------------------------------------------------- int CONFIGPROTO::SendHdrAnswer(int sock, int err) { if (err) { if (send(sock, ERR_HEADER, sizeof(ERR_HEADER) - 1, 0) < 0) { logger("send error: %s", strerror(errno)); return -1; } } else { if (send(sock, OK_HEADER, sizeof(OK_HEADER) - 1, 0) < 0) { logger("send error: %s", strerror(errno)); return -1; } } return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::RecvLogin(int sock) { char login[ADM_LOGIN_LEN + 1]; memset(login, 0, ADM_LOGIN_LEN + 1); size_t pos = 0; while (pos < ADM_LOGIN_LEN) { if (!WaitPackets(sock)) { state = confHdr; return ENODATA; } ssize_t ret = recv(sock, &login[pos], ADM_LOGIN_LEN - static_cast(pos), 0); if (ret <= 0) { // Error in network logger("recv error: %s", strerror(errno)); state = confHdr; return ENODATA; } pos += ret; } if (admins->Find(login, &currAdmin)) { // Admin not found state = confHdr; return ENODATA; } currAdmin->SetIP(adminIP); adminLogin = login; state = confLoginCipher; return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::SendLoginAnswer(int sock) { if (send(sock, OK_LOGIN, sizeof(OK_LOGIN) - 1, 0) < 0) { logger("Send OK_LOGIN error in SendLoginAnswer."); return -1; } return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::RecvLoginS(int sock) { char loginS[ADM_LOGIN_LEN + 1]; memset(loginS, 0, ADM_LOGIN_LEN + 1); size_t pos = 0; while (pos < ADM_LOGIN_LEN) { if (!WaitPackets(sock)) { state = confHdr; return ENODATA; } ssize_t ret = recv(sock, &loginS[pos], ADM_LOGIN_LEN - static_cast(pos), 0); if (ret <= 0) { // Network error printfd(__FILE__, "recv error: '%s'\n", strerror(errno)); logger("recv error: %s", strerror(errno)); state = confHdr; return ENODATA; } pos += ret; } if (currAdmin->GetLogin().empty()) { state = confHdr; return ENODATA; } BLOWFISH_CTX ctx; InitContext(currAdmin->GetPassword().c_str(), ADM_PASSWD_LEN, &ctx); char login[ADM_LOGIN_LEN + 1]; for (size_t i = 0; i < ADM_LOGIN_LEN / 8; i++) DecryptBlock(login + i * 8, loginS + i * 8, &ctx); if (currAdmin == admins->GetNoAdmin()) { // If there are no admins registered in the system - give access with any password state = confData; return 0; } if (strncmp(currAdmin->GetLogin().c_str(), login, ADM_LOGIN_LEN) != 0) { state = confHdr; return ENODATA; } state = confData; adminPassword = currAdmin->GetPassword(); return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::SendLoginSAnswer(int sock, int err) { if (err) { if (send(sock, ERR_LOGINS, sizeof(ERR_LOGINS) - 1, 0) < 0) { logger("send error: %s", strerror(errno)); return -1; } } else { if (send(sock, OK_LOGINS, sizeof(OK_LOGINS) - 1, 0) < 0) { logger("send error: %s", strerror(errno)); return -1; } } return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::RecvData(int sock) { requestList.clear(); BLOWFISH_CTX ctx; InitContext(currAdmin->GetPassword().c_str(), ADM_PASSWD_LEN, &ctx); while (1) { bool done = false; char bufferS[8]; size_t pos = 0; while (pos < sizeof(bufferS)) { if (!WaitPackets(sock)) { done = true; break; } ssize_t ret = recv(sock, &bufferS[pos], sizeof(bufferS) - static_cast(pos), 0); if (ret < 0) { // Network error logger("recv error: %s", strerror(errno)); printfd(__FILE__, "recv error: '%s'\n", strerror(errno)); return -1; } if (ret == 0) { done = true; break; } pos += ret; } char buffer[8]; buffer[7] = 0; DecryptBlock(buffer, bufferS, &ctx); requestList.push_back(std::string(buffer, pos)); if (done || memchr(buffer, 0, pos) != NULL) { // End of data if (ParseCommand()) return SendError(sock, "Bad command"); else return SendDataAnswer(sock, GetDataAnswer()); } } //return 0; } //----------------------------------------------------------------------------- int CONFIGPROTO::SendDataAnswer(int sock, const std::string & answer) { if (answer.empty()) return 0; BLOWFISH_CTX ctx; InitContext(adminPassword.c_str(), ADM_PASSWD_LEN, &ctx); std::string::size_type pos = 0; std::string::size_type length = answer.length() + 1; while (pos < length) { char buffer[1024]; std::string::size_type chunkLength = std::min(length - pos, sizeof(buffer)); EncryptString(buffer, answer.c_str() + pos, chunkLength, &ctx); if (send(sock, buffer, (chunkLength & ~7) < chunkLength ? chunkLength + 8 : chunkLength, 0) < 0) // Need to send data adjusted to the 8-byte boundary. return -1; pos += chunkLength; } return 1; } //----------------------------------------------------------------------------- int CONFIGPROTO::SendError(int sock, const std::string & text) { return SendDataAnswer(sock, ""); } //----------------------------------------------------------------------------- void CONFIGPROTO::WriteLogAccessFailed(uint32_t ip) { logger("Admin's connection failed. IP %s", inet_ntostring(ip).c_str()); } //-----------------------------------------------------------------------------