Files
digFTP/src/main.cpp
2024-10-17 20:02:54 -05:00

214 lines
5.4 KiB
C++

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <future>
#include <thread>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include "main.h"
#include "server.h"
#include "client.cpp"
#include "util.h"
using namespace std::chrono_literals;
const uint16_t max_clients = config->getInt("net", "max_clients", 255);
const uint16_t cport = config->getInt("net", "control_port", 21);
struct pollfd fds[65535];
struct clientfd {
Client* client;
bool close = false;
} fdc[65535];
bool run = true, compress_array = false;
void runClient(struct clientfd* cfd) {
char inbuf[BUFFERSIZE];
//printf("[d] C(%i) Initialized\n", cfd->client->control_sock);
// Loop as long as it is a valid file descriptor.
try {
//while (fcntl(cfd->client->control_sock, F_GETFD) != -1) {
while (true) {
if (cfd->client == nullptr) { break; }
int rc = recv(cfd->client->control_sock, inbuf, sizeof(inbuf), 0);
if (rc < 0) {
logger->print(LOGLEVEL_WARNING, "C(%i) Recieved empty packet", cfd->client->control_sock);
if (errno != EWOULDBLOCK) {
perror("recv() failed");
break;
}
continue;
}
if (rc == 0 || cfd->client == nullptr) {
//printf("[d] C(%i) closed\n", cfd->client->control_sock);
break;
}
std::string lin(inbuf);
int len = lin.find("\r\n", 0);
int cmdend = lin.find(" ", 0);
if (cmdend >= len || cmdend == std::string::npos) cmdend = len;
std::string cmd = toUpper(lin.substr(0, cmdend));
std::string args = "";
if (len > cmdend) args = lin.substr(cmdend+1, len-cmdend-1);
logger->print(LOGLEVEL_DEBUG, "C(%i) >> '%s' '%s'", cfd->client->control_sock, cmd.c_str(), args.c_str());
if (cfd->client->receive(cmd, args) < 0) break;
inbuf[0] = '\0';
}
} catch (...) {
logger->print(LOGLEVEL_ERROR, "C(%i) Caught error!", cfd->client->control_sock);
}
cfd->close = true;
}
int main(int argc , char *argv[]) {
logger = new Logger(config->getValue("logging", "file", "digftp.log").c_str());
logger->setLevel(config->getInt("logging", "level", 0));
std::string authType = config->getValue("main", "auth_engine", "plain");
auth = getAuthByName(authType);
auth->setOptions(config->get(authType));
int opt = 1,
master_socket = -1,
newsock = -1,
nfds = 1,
current_size = 0;
struct sockaddr_in ctrl_address;
if ((master_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket() failed");
exit(-1);
}
if (setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) {
perror("setsockopt() failed");
close(master_socket);
exit(-1);
}
if (ioctl(master_socket, FIONBIO, (char *)&opt) < 0) {
perror("ioctl() failed");
close(master_socket);
exit(-1);
}
ctrl_address.sin_family = AF_INET;
ctrl_address.sin_addr.s_addr = INADDR_ANY;
ctrl_address.sin_port = htons(cport);
if (bind(master_socket, (struct sockaddr *)&ctrl_address, sizeof(ctrl_address))<0 < 0) {
perror("bind() failed");
close(master_socket);
exit(-1);
}
if (listen(master_socket, 3) < 0) {
perror("listen() failed");
close(master_socket);
exit(-1);
}
memset(fds, 0, sizeof(fds));
memset(fdc, 0, sizeof(fdc));
fds[0].fd = master_socket;
fds[0].events = POLLIN;
logger->print(LOGLEVEL_INFO, "Server started.");
while (run) {
int pc = poll(fds, nfds, -1);
if (pc < 0) {
perror("poll() failed");
break;
}
if (pc == 0) {
perror("poll() timed out\n");
break;
}
current_size = nfds;
for (int i = 0; i < current_size; i++) {
if(fds[i].revents == 0)
continue;
if(fds[i].revents != POLLIN) {
logger->print(LOGLEVEL_ERROR, "C(%i) Error! revents = %d", fds[i].fd, fds[i].revents);
goto conn_close;
}
if (fds[i].fd == master_socket) {
do {
newsock = accept(master_socket, NULL, NULL);
if (newsock < 0) {
if (errno != EWOULDBLOCK) {
perror("accept() failed");
run = false;
}
break;
}
logger->print(LOGLEVEL_DEBUG, "C(%i) Accepted client", newsock);
fds[nfds].fd = newsock;
fds[nfds].events = POLLIN;
fdc[nfds].close = false;
fdc[nfds].client = new Client(newsock);
fdc[nfds].client->thread = std::thread(runClient, &fdc[nfds]);
nfds++;
} while (newsock != -1);
} else {
if (fdc[i].close) {
conn_close:
logger->print(LOGLEVEL_DEBUG, "C(%i) Deleting client...", fds[i].fd);
close(fds[i].fd);
fds[i].fd = -1;
if (fdc[i].client->thread.joinable())
fdc[i].client->thread.detach();
fdc[i].client = nullptr;
fdc[i].close = false;
compress_array = true;
} else {
fds[i].revents = 0;
}
}
}
if (compress_array) {
compress_array = false;
for (int i = 0; i < nfds; i++) {
if (fds[i].fd == -1) {
for(int j = i; j < nfds; j++) {
if (fds[j].fd == -1) {
logger->print(LOGLEVEL_DEBUG, "Compressing: %i(fd:%i) <= %i(fd:%i)", j, fds[j].fd, j+1, fds[j+1].fd);
fds[j].fd = fds[j+1].fd;
fds[j].revents = fds[j+1].revents;
fds[j+1].fd = -1;
logger->print(LOGLEVEL_DEBUG, "Reinitialized fds of %i", j+1);
fdc[j].client = fdc[j+1].client;
fdc[j].close = fdc[j+1].close;
fdc[j+1] = {};
logger->print(LOGLEVEL_DEBUG, "[d] Reinitialized fdc of %i", j+1);
}
}
i--;
nfds--;
}
}
}
}
logger->close();
return 0;
}