rename
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.26)
|
CMAKE_MINIMUM_REQUIRED(VERSION 3.26)
|
||||||
PROJECT(worlioftp)
|
PROJECT(digftp)
|
||||||
|
|
||||||
SET(CMAKE_CXX_STANDARD 20)
|
SET(CMAKE_CXX_STANDARD 20)
|
||||||
|
SET(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
#SET(CMAKE_VERBOSE_MAKEFILE ON)
|
SET(CMAKE_CXX_FLAGS "-O3")
|
||||||
|
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
#ADD_DEFINITIONS(-Wall -O2)
|
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
ADD_SUBDIRECTORY(src)
|
ADD_SUBDIRECTORY(src)
|
||||||
|
FILE(COPY conf DESTINATION ${CMAKE_BINARY_DIR})
|
||||||
|
INSTALL(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
|
||||||
15
conf/ftp.conf
Normal file
15
conf/ftp.conf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[main]
|
||||||
|
server_name=My FTP Server
|
||||||
|
motd_file=motd
|
||||||
|
auth_engine=noauth
|
||||||
|
[net]
|
||||||
|
listen_address=127.0.0.1
|
||||||
|
control_port=21
|
||||||
|
max_clients=255
|
||||||
|
[noauth]
|
||||||
|
user_root=/home/%u/
|
||||||
|
chroot=1
|
||||||
|
[passdb]
|
||||||
|
file=conf/passdb
|
||||||
|
user_root=/home/%u/
|
||||||
|
case_sensitive=1
|
||||||
1
conf/passdb
Normal file
1
conf/passdb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
anonymous:anonymous@
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
[general]
|
|
||||||
name=Worlio FTP
|
|
||||||
contact=admin@localhost
|
|
||||||
location=US
|
|
||||||
motd=Welcome!
|
|
||||||
version=0.0
|
|
||||||
path=/data/ftp-test
|
|
||||||
|
|
||||||
[network]
|
|
||||||
address=0.0.0.0
|
|
||||||
port=21
|
|
||||||
data-port=20
|
|
||||||
active=true
|
|
||||||
passive=true
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
add_executable(
|
add_executable(
|
||||||
worlioftp
|
${PROJECT_NAME}
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
27
src/auth.h
Normal file
27
src/auth.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef AUTHMETHODS_H
|
||||||
|
#define AUTHMETHODS_H
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
public:
|
||||||
|
Auth() {};
|
||||||
|
|
||||||
|
virtual void setOptions(ConfigSection* options) {};
|
||||||
|
|
||||||
|
virtual bool isChroot() { return false; };
|
||||||
|
|
||||||
|
virtual bool check(std::string username, std::string password) { return false; };
|
||||||
|
|
||||||
|
virtual std::string getUserDirectory(std::string username) { return ""; };
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "auth/noauth.h"
|
||||||
|
#include "auth/passdb.h"
|
||||||
|
|
||||||
|
Auth* getAuthByName(std::string name) {
|
||||||
|
if (name == "noauth") return new NoAuth();
|
||||||
|
if (name == "passdb") return new PassdbAuth();
|
||||||
|
return new Auth();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
32
src/auth/noauth.h
Normal file
32
src/auth/noauth.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef _AUTH_NOAUTH
|
||||||
|
#define _AUTH_NOAUTH
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
class NoAuth : public Auth {
|
||||||
|
public:
|
||||||
|
NoAuth() {};
|
||||||
|
|
||||||
|
void setOptions(ConfigSection* options) {
|
||||||
|
this->options = options;
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigSection* getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isChroot() {
|
||||||
|
return options->getInt("chroot", 1) == 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool check(std::string username, std::string password) { return true; };
|
||||||
|
|
||||||
|
std::string getUserDirectory(std::string username) {
|
||||||
|
std::string authDir = options->getValue("user_root", "/home/%u/");
|
||||||
|
authDir = replace(authDir, "%u", username);
|
||||||
|
return authDir;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
ConfigSection* options;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
43
src/auth/passdb.h
Normal file
43
src/auth/passdb.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef _AUTH_PASSDB
|
||||||
|
#define _AUTH_PASSDB
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
class PassdbAuth : public Auth {
|
||||||
|
public:
|
||||||
|
PassdbAuth() {};
|
||||||
|
|
||||||
|
void setOptions(ConfigSection* options) {
|
||||||
|
std::ifstream file(options->getValue("file", "passdb"));
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.length() == 0) continue;
|
||||||
|
else if (line.rfind("#", 0) == 0) continue;
|
||||||
|
else {
|
||||||
|
std::string username = line.substr(0, line.find(":"));
|
||||||
|
if (options->getInt("case_sensitive", 1) != 0)
|
||||||
|
username = toLower(username);
|
||||||
|
userlist[username] = line.substr(line.find(":")+1, line.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userRoot = options->getValue("user_root", "/home/%u/");
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isChroot() { return true; };
|
||||||
|
|
||||||
|
bool check(std::string username, std::string password) {
|
||||||
|
if (userlist.find(username) == userlist.end()) return false;
|
||||||
|
if (userlist[username] != password) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string getUserDirectory(std::string username) {
|
||||||
|
std::string authDir = userRoot;
|
||||||
|
authDir = replace(authDir, "%u", username);
|
||||||
|
return authDir;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::string> userlist;
|
||||||
|
std::string userRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "filer.cpp"
|
#include "filer.cpp"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -30,7 +31,7 @@ public:
|
|||||||
|
|
||||||
Client(int &_sock) {
|
Client(int &_sock) {
|
||||||
control_sock = _sock;
|
control_sock = _sock;
|
||||||
submit(230, "Init");
|
submit(230, config->getValue("main", "server_name", "digFTP Server"));
|
||||||
};
|
};
|
||||||
|
|
||||||
int receive(std::string cmd, std::string argstr) {
|
int receive(std::string cmd, std::string argstr) {
|
||||||
@@ -44,29 +45,17 @@ public:
|
|||||||
submit(257, "'"+filer->cwd.string()+"'");
|
submit(257, "'"+filer->cwd.string()+"'");
|
||||||
} else if (cmd == "CWD") {
|
} else if (cmd == "CWD") {
|
||||||
int rc = filer->traverse(argstr);
|
int rc = filer->traverse(argstr);
|
||||||
if (rc == 0) {
|
if (rc == 0) submit(250, "OK");
|
||||||
submit(250, "OK");
|
else submit(431, "No such directory");
|
||||||
} else if (rc == -1) {
|
|
||||||
submit(500, "Not found!");
|
|
||||||
} else {
|
|
||||||
submit(500, std::to_string(rc));
|
|
||||||
}
|
|
||||||
} else if (cmd == "CDUP") {
|
} else if (cmd == "CDUP") {
|
||||||
int rc = filer->traverse("..");
|
int rc = filer->traverse("..");
|
||||||
if (rc == 0) {
|
if (rc == 0) submit(250, "OK");
|
||||||
submit(250, "OK");
|
else submit(431, "No such directory");
|
||||||
} else if (rc == -1) {
|
|
||||||
submit(500, "Not found!");
|
|
||||||
} else {
|
|
||||||
submit(500, std::to_string(rc));
|
|
||||||
}
|
|
||||||
} else if (cmd == "MKD") {
|
} else if (cmd == "MKD") {
|
||||||
int rc = filer->createDirectory(argstr);
|
int rc = filer->createDirectory(argstr);
|
||||||
if (rc == 0) {
|
if (rc == 0) submit(257, "\""+filer->relPath(argstr).string()+"\" directory created");
|
||||||
submit(250, "OK");
|
else if (rc == -1) submit(521, "Directory already exists");
|
||||||
} else {
|
else submit(550, "Access Denied");
|
||||||
submit(500, std::to_string(rc));
|
|
||||||
}
|
|
||||||
} else if (cmd == "TYPE") {
|
} else if (cmd == "TYPE") {
|
||||||
sscanf(argstr.c_str(), "%c", &(filer->type));
|
sscanf(argstr.c_str(), "%c", &(filer->type));
|
||||||
submit(226, "OK");
|
submit(226, "OK");
|
||||||
@@ -104,10 +93,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (getsockname(data_fd, (struct sockaddr *)&data_address, &datalen) == 0) {
|
if (getsockname(data_fd, (struct sockaddr *)&data_address, &datalen) == 0) {
|
||||||
//memcpy(&netaddr[0], &data_address.sin_addr, 4);
|
sscanf(config->getValue("net", "listen_address", "127.0.0.1").c_str(), "%d.%d.%d.%d", &netaddr[0], &netaddr[1], &netaddr[2], &netaddr[3]);
|
||||||
netaddr[0] = 127;
|
|
||||||
netaddr[1] = netaddr[2] = 0;
|
|
||||||
netaddr[3] = 1;
|
|
||||||
dataport = ntohs(data_address.sin_port);
|
dataport = ntohs(data_address.sin_port);
|
||||||
memcpy(&netport[0], &dataport, 2);
|
memcpy(&netport[0], &dataport, 2);
|
||||||
printf("[i] D(%i) PASV initialized: %u.%u.%u.%u:%u\n", data_fd, netaddr[0], netaddr[1], netaddr[2], netaddr[3], dataport);
|
printf("[i] D(%i) PASV initialized: %u.%u.%u.%u:%u\n", data_fd, netaddr[0], netaddr[1], netaddr[2], netaddr[3], dataport);
|
||||||
@@ -120,7 +106,7 @@ public:
|
|||||||
char* pasvok;
|
char* pasvok;
|
||||||
asprintf(
|
asprintf(
|
||||||
&pasvok,
|
&pasvok,
|
||||||
"Entering Passive Mode (%u,%u,%u,%u,%u,%u)",
|
"Entering Passive Mode (%u,%u,%u,%u,%u,%u).",
|
||||||
netaddr[0],
|
netaddr[0],
|
||||||
netaddr[1],
|
netaddr[1],
|
||||||
netaddr[2],
|
netaddr[2],
|
||||||
@@ -135,7 +121,6 @@ public:
|
|||||||
if ((data_sock = accept(data_fd, NULL, NULL)) >= 0) {
|
if ((data_sock = accept(data_fd, NULL, NULL)) >= 0) {
|
||||||
printf("[i] D(%i) PASV accepted: %i\n", data_fd, data_sock);
|
printf("[i] D(%i) PASV accepted: %i\n", data_fd, data_sock);
|
||||||
state = 2;
|
state = 2;
|
||||||
//submit(200, "OK");
|
|
||||||
} else {
|
} else {
|
||||||
perror("accept() failed");
|
perror("accept() failed");
|
||||||
submit(425, "Unknown Error");
|
submit(425, "Unknown Error");
|
||||||
@@ -177,7 +162,7 @@ public:
|
|||||||
} else if (cmd == "DELE" || cmd == "RMD") {
|
} else if (cmd == "DELE" || cmd == "RMD") {
|
||||||
int ret = filer->deleteFile(argstr);
|
int ret = filer->deleteFile(argstr);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
submit(226, "OK");
|
submit(250, "OK");
|
||||||
} else {
|
} else {
|
||||||
submit(550, "Access Denied "+std::to_string(ret));
|
submit(550, "Access Denied "+std::to_string(ret));
|
||||||
}
|
}
|
||||||
@@ -197,13 +182,12 @@ public:
|
|||||||
if (fd.ecode == 0) {
|
if (fd.ecode == 0) {
|
||||||
submit(150, "Transferring");
|
submit(150, "Transferring");
|
||||||
data_submit(fd.data, fd.size);
|
data_submit(fd.data, fd.size);
|
||||||
|
free(fd.data);
|
||||||
|
submit(226, "OK");
|
||||||
} else {
|
} else {
|
||||||
if (fd.ecode == -3) submit(550, "I refuse to transmit in ASCII mode!");
|
if (fd.ecode == -3) submit(550, "I refuse to transmit in ASCII mode!");
|
||||||
else submit(550, "Access Denied");
|
else submit(550, "Access Denied");
|
||||||
}
|
}
|
||||||
// Segfault on freeing ASCII data, memory leak on binary.
|
|
||||||
if (filer->type != 'A') free(fd.data);
|
|
||||||
submit(226, "OK");
|
|
||||||
} else if (cmd == "STOR") {
|
} else if (cmd == "STOR") {
|
||||||
unsigned char inbuf[BUFFERSIZE] = {0};
|
unsigned char inbuf[BUFFERSIZE] = {0};
|
||||||
submit(150, "Transferring");
|
submit(150, "Transferring");
|
||||||
@@ -232,20 +216,21 @@ public:
|
|||||||
name = argstr;
|
name = argstr;
|
||||||
submit(331, "Password required");
|
submit(331, "Password required");
|
||||||
} else if (cmd == "PASS") {
|
} else if (cmd == "PASS") {
|
||||||
if (argstr == "123") {
|
if (auth->check(name, argstr)) {
|
||||||
printf("[d] (%i) logged in as '%s'\n", control_sock, name.c_str());
|
printf("[d] (%i) logged in as '%s'\n", control_sock, name.c_str());
|
||||||
// We can now set the root safely (I hope).
|
// We can now set the root safely (I hope).
|
||||||
filer->setRoot("/data/ftp-test/"+name);
|
filer->setRoot(auth->getUserDirectory(name));
|
||||||
state = 1;
|
state = 1;
|
||||||
submit(230, "Login successful");
|
submit(230, "Login OK");
|
||||||
} else {
|
} else {
|
||||||
submit(530, "Authentication refused.");
|
name = "";
|
||||||
return -1;
|
submit(530, "Invalid Credentials.");
|
||||||
}
|
}
|
||||||
} else if (cmd == "AUTH") {
|
} else if (cmd == "AUTH") {
|
||||||
submit(502, "Command not implemented");
|
submit(502, "Command not implemented");
|
||||||
} else {
|
} else {
|
||||||
submit(332, "Login Required");
|
submit(332, "Not Logged In!");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -291,4 +276,4 @@ private:
|
|||||||
int data_fd;
|
int data_fd;
|
||||||
int data_sock;
|
int data_sock;
|
||||||
struct sockaddr_in data_address;
|
struct sockaddr_in data_address;
|
||||||
};
|
};
|
||||||
|
|||||||
116
src/conf.h
Normal file
116
src/conf.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#ifndef CONF_H
|
||||||
|
#define CONF_H
|
||||||
|
#include <map>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class ConfigSection {
|
||||||
|
public:
|
||||||
|
ConfigSection() {};
|
||||||
|
ConfigSection(std::map<std::string, std::string> options) {
|
||||||
|
this->options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> get() {
|
||||||
|
return this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getKeyValues() {
|
||||||
|
std::vector<std::string> values;
|
||||||
|
for (auto const& c : this->options) {
|
||||||
|
values.push_back(c.first);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getValue(std::string name, std::string def) {
|
||||||
|
if (this->options.find(name) != this->options.end()) return this->options[name];
|
||||||
|
else return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getInt(std::string name, int def) {
|
||||||
|
if (this->options.find(name) != this->options.end()) return std::stoi(this->options[name]);
|
||||||
|
else return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValue(std::string name, std::string value) {
|
||||||
|
this->options[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInt(std::string name, int value) {
|
||||||
|
this->options[name] = std::to_string(value);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::string> options;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigFile {
|
||||||
|
public:
|
||||||
|
ConfigFile() {};
|
||||||
|
ConfigFile(std::string path) {
|
||||||
|
std::ifstream file(path);
|
||||||
|
ConfigSection* section;
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.length() == 0) continue;
|
||||||
|
else if (line.rfind("#", 0) == 0) continue;
|
||||||
|
else if (line.rfind("[", 0) == 0) {
|
||||||
|
section = new ConfigSection();
|
||||||
|
this->sections[line.substr(1, line.find("]")-1)] = section;
|
||||||
|
} else if (line.find("=") != std::string::npos) {
|
||||||
|
section->setValue(line.substr(0, line.find("=")), line.substr(line.find("=")+1, line.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Configuration loaded: \"%s\"\n", path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, ConfigSection*> get() {
|
||||||
|
return this->sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSection* get(std::string section) {
|
||||||
|
return this->sections[section];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getKeyValues() {
|
||||||
|
std::vector<std::string> values;
|
||||||
|
for (auto const& c : this->sections) {
|
||||||
|
values.push_back(c.first);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getValue(std::string section, std::string name, std::string def) {
|
||||||
|
if (this->sections[section]->get().find(name) != this->sections[section]->get().end()) return this->sections[section]->getValue(name, def);
|
||||||
|
else return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getInt(std::string section, std::string name, int def) {
|
||||||
|
if (this->sections[section]->get().find(name) != this->sections[section]->get().end()) return this->sections[section]->getInt(name, def);
|
||||||
|
else return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValue(std::string section, std::string name, std::string value) {
|
||||||
|
this->sections[section]->setValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInt(std::string section, std::string name, int value) {
|
||||||
|
this->sections[section]->setInt(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(std::string path) {
|
||||||
|
std::ofstream outfile(path);
|
||||||
|
for (std::map<std::string, ConfigSection*>::iterator si = this->sections.begin(); si != this->sections.end(); ++si) {
|
||||||
|
outfile << "[" << si->first << "]" << std::endl;
|
||||||
|
for (std::map<std::string, std::string>::iterator it = si->second->get().begin(); it != si->second->get().end(); ++it) {
|
||||||
|
outfile << it->first << "=" << it->second << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outfile.close();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::map<std::string, ConfigSection*> sections;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -78,6 +78,14 @@ public:
|
|||||||
return fs::weakly_canonical(root.string()+"/"+(cwd / in).string());
|
return fs::weakly_canonical(root.string()+"/"+(cwd / in).string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::path relPath() {
|
||||||
|
return fs::weakly_canonical("/"+cwd.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path relPath(std::string in) {
|
||||||
|
return fs::weakly_canonical("/"+(cwd / in).string());
|
||||||
|
}
|
||||||
|
|
||||||
int fileSize(std::string name) {
|
int fileSize(std::string name) {
|
||||||
fs::path nfile = fs::weakly_canonical(cwd / name);
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
fs::path ffile = fullPath(name);
|
fs::path ffile = fullPath(name);
|
||||||
@@ -114,19 +122,7 @@ public:
|
|||||||
printf("[i] Retreiving file: %s\n", nfile.c_str());
|
printf("[i] Retreiving file: %s\n", nfile.c_str());
|
||||||
if (ffile.string().rfind(root.string(), 0) != 0) fd.ecode = -2;
|
if (ffile.string().rfind(root.string(), 0) != 0) fd.ecode = -2;
|
||||||
else if (!fs::exists(ffile)) fd.ecode = -1;
|
else if (!fs::exists(ffile)) fd.ecode = -1;
|
||||||
else if (type == 'A') {
|
else if (type != 'A') {
|
||||||
std::ifstream infile(ffile, std::ios::in|std::ios::ate);
|
|
||||||
if (infile.is_open()) {
|
|
||||||
std::string filedata;
|
|
||||||
for (std::string line; getline(infile, line);) {
|
|
||||||
filedata+=(line+"\r\n");
|
|
||||||
}
|
|
||||||
fd.size = filedata.size();
|
|
||||||
strncpy(fd.data, filedata.c_str(), fd.size);
|
|
||||||
infile.close();
|
|
||||||
fd.ecode = 0;
|
|
||||||
} else fd.ecode = -2;
|
|
||||||
} else {
|
|
||||||
std::ifstream infile(ffile, std::ios::in|std::ios::binary|std::ios::ate);
|
std::ifstream infile(ffile, std::ios::in|std::ios::binary|std::ios::ate);
|
||||||
if (infile.is_open()) {
|
if (infile.is_open()) {
|
||||||
fd.size = infile.tellg();
|
fd.size = infile.tellg();
|
||||||
@@ -136,7 +132,7 @@ public:
|
|||||||
infile.close();
|
infile.close();
|
||||||
fd.ecode = 0;
|
fd.ecode = 0;
|
||||||
} else fd.ecode = -2;
|
} else fd.ecode = -2;
|
||||||
}
|
} else fd.ecode = -3;
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +149,6 @@ public:
|
|||||||
outfile.write((char *)data, size);
|
outfile.write((char *)data, size);
|
||||||
outfile.close();
|
outfile.close();
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -167,7 +162,6 @@ public:
|
|||||||
outfile.write((char *)data, size);
|
outfile.write((char *)data, size);
|
||||||
outfile.close();
|
outfile.close();
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
90
src/main.cpp
90
src/main.cpp
@@ -15,67 +15,73 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "client.cpp"
|
#include "client.cpp"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// I stole the majority* of this multi-client sock code from IBM
|
const int max_clients = config->getInt("net", "max_clients", 255);
|
||||||
// of all places. Had to still modify it for my own uses but
|
const int cport = config->getInt("net", "control_port", 21);
|
||||||
// laziness wins again!
|
|
||||||
|
|
||||||
struct pollfd fds[MAXCLIENTS];
|
struct pollfd fds[65535];
|
||||||
struct clientfd {
|
struct clientfd {
|
||||||
Client* client;
|
Client* client;
|
||||||
bool close = false;
|
bool close = false;
|
||||||
} fdc[MAXCLIENTS];
|
} fdc[65535];
|
||||||
|
|
||||||
bool run = true, compress_array = false;
|
bool run = true, compress_array = false;
|
||||||
|
|
||||||
void runClient(struct clientfd* cfd) {
|
void runClient(struct clientfd* cfd) {
|
||||||
char inbuf[BUFFERSIZE];
|
char inbuf[BUFFERSIZE];
|
||||||
printf("[d] C(%i) Initialized\n", cfd->client->control_sock);
|
//printf("[d] C(%i) Initialized\n", cfd->client->control_sock);
|
||||||
// Loop as long as it is a valid file descriptor.
|
// Loop as long as it is a valid file descriptor.
|
||||||
try {
|
try {
|
||||||
while (fcntl(cfd->client->control_sock, F_GETFD) != -1) {
|
//while (fcntl(cfd->client->control_sock, F_GETFD) != -1) {
|
||||||
printf("[d] C(%i) Attempting read...\n", cfd->client->control_sock);
|
while (true) {
|
||||||
int rc = recv(cfd->client->control_sock, inbuf, sizeof(inbuf), 0);
|
if (cfd->client == nullptr) { break; }
|
||||||
if (rc < 0) {
|
printf("[d] C(%i) Attempting read...\n", cfd->client->control_sock);
|
||||||
if (errno != EWOULDBLOCK) {
|
int rc = recv(cfd->client->control_sock, inbuf, sizeof(inbuf), 0);
|
||||||
perror("recv() failed");
|
if (rc < 0) {
|
||||||
|
printf("[d] C(%i) Recieved empty packet\n", 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;
|
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);
|
|
||||||
|
|
||||||
printf("[d] C(%i) >> '%s' '%s'\n", cfd->client->control_sock, cmd.c_str(), args.c_str());
|
std::string lin(inbuf);
|
||||||
|
int len = lin.find("\r\n", 0);
|
||||||
if (cfd->client->receive(cmd, args) < 0) break;
|
int cmdend = lin.find(" ", 0);
|
||||||
inbuf[0] = '\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);
|
||||||
|
|
||||||
|
printf("[d] C(%i) >> '%s' '%s'\n", cfd->client->control_sock, cmd.c_str(), args.c_str());
|
||||||
|
|
||||||
|
if (cfd->client->receive(cmd, args) < 0) break;
|
||||||
|
inbuf[0] = '\0';
|
||||||
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
printf("[!] C(%i) Caught error!\n", cfd->client->control_sock);
|
printf("[!] C(%i) Caught error!\n", cfd->client->control_sock);
|
||||||
}
|
}
|
||||||
cfd->client->thread.detach();
|
//printf("[d] C(%i) Marking for deletion...\n", cfd->client->control_sock);
|
||||||
printf("[d] C(%i) Marking for deletion...\n", cfd->client->control_sock);
|
|
||||||
cfd->close = true;
|
cfd->close = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc , char *argv[]) {
|
int main(int argc , char *argv[]) {
|
||||||
|
std::string authType = config->getValue("main", "auth_engine", "plain");
|
||||||
|
auth = getAuthByName(authType);
|
||||||
|
auth->setOptions(config->get(authType));
|
||||||
|
|
||||||
int opt = 1,
|
int opt = 1,
|
||||||
master_socket = -1,
|
master_socket = -1,
|
||||||
newsock = -1,
|
newsock = -1,
|
||||||
@@ -103,7 +109,7 @@ int main(int argc , char *argv[]) {
|
|||||||
|
|
||||||
ctrl_address.sin_family = AF_INET;
|
ctrl_address.sin_family = AF_INET;
|
||||||
ctrl_address.sin_addr.s_addr = INADDR_ANY;
|
ctrl_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
ctrl_address.sin_port = htons(CONTROL_PORT);
|
ctrl_address.sin_port = htons(cport);
|
||||||
|
|
||||||
if (bind(master_socket, (struct sockaddr *)&ctrl_address, sizeof(ctrl_address))<0 < 0) {
|
if (bind(master_socket, (struct sockaddr *)&ctrl_address, sizeof(ctrl_address))<0 < 0) {
|
||||||
perror("bind() failed");
|
perror("bind() failed");
|
||||||
@@ -117,8 +123,8 @@ int main(int argc , char *argv[]) {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(fds, 0 , sizeof(fds));
|
memset(fds, 0, sizeof(fds));
|
||||||
memset(fdc, 0 , sizeof(fdc));
|
memset(fdc, 0, sizeof(fdc));
|
||||||
|
|
||||||
fds[0].fd = master_socket;
|
fds[0].fd = master_socket;
|
||||||
fds[0].events = POLLIN;
|
fds[0].events = POLLIN;
|
||||||
@@ -146,7 +152,6 @@ int main(int argc , char *argv[]) {
|
|||||||
printf("[!] C(%i) Error! revents = %d\n", fds[i].fd, fds[i].revents);
|
printf("[!] C(%i) Error! revents = %d\n", fds[i].fd, fds[i].revents);
|
||||||
goto conn_close;
|
goto conn_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[i].fd == master_socket) {
|
if (fds[i].fd == master_socket) {
|
||||||
do {
|
do {
|
||||||
newsock = accept(master_socket, NULL, NULL);
|
newsock = accept(master_socket, NULL, NULL);
|
||||||
@@ -177,10 +182,11 @@ int main(int argc , char *argv[]) {
|
|||||||
fdc[i].client = nullptr;
|
fdc[i].client = nullptr;
|
||||||
fdc[i].close = false;
|
fdc[i].close = false;
|
||||||
compress_array = true;
|
compress_array = true;
|
||||||
|
} else {
|
||||||
|
fds[i].revents = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compress_array) {
|
if (compress_array) {
|
||||||
compress_array = false;
|
compress_array = false;
|
||||||
for (int i = 0; i < nfds; i++) {
|
for (int i = 0; i < nfds; i++) {
|
||||||
@@ -189,7 +195,13 @@ int main(int argc , char *argv[]) {
|
|||||||
if (fds[j].fd == -1) {
|
if (fds[j].fd == -1) {
|
||||||
printf("[d] Compressing: %i(fd:%i) <= %i(fd:%i)\n", j, fds[j].fd, j+1, fds[j+1].fd);
|
printf("[d] Compressing: %i(fd:%i) <= %i(fd:%i)\n", j, fds[j].fd, j+1, fds[j+1].fd);
|
||||||
fds[j].fd = fds[j+1].fd;
|
fds[j].fd = fds[j+1].fd;
|
||||||
|
fds[j].revents = fds[j+1].revents;
|
||||||
|
fds[j+1].fd = -1;
|
||||||
|
printf("[d] Reinitialized fds of %i\n", j+1);
|
||||||
fdc[j].client = fdc[j+1].client;
|
fdc[j].client = fdc[j+1].client;
|
||||||
|
fdc[j].close = fdc[j+1].close;
|
||||||
|
fdc[j+1] = {};
|
||||||
|
printf("[d] Reinitialized fdc of %i\n", j+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i--;
|
i--;
|
||||||
|
|||||||
10
src/main.h
Normal file
10
src/main.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef MAIN_H
|
||||||
|
#define MAIN_H
|
||||||
|
#include "conf.h"
|
||||||
|
#include "auth.h"
|
||||||
|
#include "auth/noauth.h"
|
||||||
|
|
||||||
|
ConfigFile* config = new ConfigFile("conf/ftp.conf");
|
||||||
|
Auth* auth;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
#ifndef SERVER_H
|
#ifndef SERVER_H
|
||||||
#define SERVER_H
|
#define SERVER_H
|
||||||
|
|
||||||
// BUNCH OF BULLSHIT
|
#define APPNAME "digFTP"
|
||||||
#define APPNAME "tunnelFTP (Worlio Bridge)"
|
|
||||||
#define APPVER "0.0"
|
#define APPVER "0.0"
|
||||||
#define CONTROL_PORT 21
|
|
||||||
#define DATA_PORT 20
|
|
||||||
#define BUFFERSIZE 1024*512
|
#define BUFFERSIZE 1024*512
|
||||||
#define MAXCLIENTS 256
|
|
||||||
#define BASEDIR "/data/ftp-test"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -9,6 +9,15 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
std::string replace(std::string subject, const std::string& search, const std::string& replace) {
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = subject.find(search, pos)) != std::string::npos) {
|
||||||
|
subject.replace(pos, search.length(), replace);
|
||||||
|
pos += replace.length();
|
||||||
|
}
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Out>
|
template <typename Out>
|
||||||
void split(const std::string &s, char delim, Out result, int limit) {
|
void split(const std::string &s, char delim, Out result, int limit) {
|
||||||
int it = 0;
|
int it = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user