Created Repo
This commit is contained in:
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
PROJECT(worlioftp)
|
||||||
|
|
||||||
|
SET(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
#SET(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
|
|
||||||
|
#ADD_DEFINITIONS(-Wall -O2)
|
||||||
|
|
||||||
|
ADD_SUBDIRECTORY(src)
|
||||||
14
conf/worlio-ftp.conf
Normal file
14
conf/worlio-ftp.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[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
|
||||||
4
src/CMakeLists.txt
Normal file
4
src/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
add_executable(
|
||||||
|
worlioftp
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
221
src/client.cpp
Normal file
221
src/client.cpp
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <format>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include "filer.cpp"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
int control_sock;
|
||||||
|
/* == State
|
||||||
|
* -1 - Delete
|
||||||
|
* 0 - Visitor / Unauthenticated
|
||||||
|
* 1 - Authenticated
|
||||||
|
* 2 - Waiting for data
|
||||||
|
*/
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
Client(int &_sock) {
|
||||||
|
control_sock = _sock;
|
||||||
|
submit(230, "Init");
|
||||||
|
};
|
||||||
|
|
||||||
|
int receive(std::string cmd, std::string argstr) {
|
||||||
|
if (cmd == "QUIT") {
|
||||||
|
submit(250, "Goodbye!");
|
||||||
|
return -1;
|
||||||
|
} else if (state > 0) {
|
||||||
|
if (cmd == "SYST") {
|
||||||
|
submit(215, "UNIX");
|
||||||
|
} else if (cmd == "PWD") {
|
||||||
|
submit(257, "'"+filer->cwd.string()+"'");
|
||||||
|
} else if (cmd == "CWD") {
|
||||||
|
int rc = filer->traverse(argstr);
|
||||||
|
if (rc == 0) {
|
||||||
|
submit(250, "OK");
|
||||||
|
} else if (rc == -1) {
|
||||||
|
submit(500, "Not found!");
|
||||||
|
} else {
|
||||||
|
submit(500, std::to_string(rc));
|
||||||
|
}
|
||||||
|
} else if (cmd == "CDUP") {
|
||||||
|
int rc = filer->traverse("..");
|
||||||
|
if (rc == 0) {
|
||||||
|
submit(250, "OK");
|
||||||
|
} else if (rc == -1) {
|
||||||
|
submit(500, "Not found!");
|
||||||
|
} else {
|
||||||
|
submit(500, std::to_string(rc));
|
||||||
|
}
|
||||||
|
} else if (cmd == "MKD") {
|
||||||
|
int rc = filer->createDirectory(argstr);
|
||||||
|
if (rc == 0) {
|
||||||
|
submit(250, "OK");
|
||||||
|
} else {
|
||||||
|
submit(500, std::to_string(rc));
|
||||||
|
}
|
||||||
|
} else if (cmd == "TYPE") {
|
||||||
|
sscanf(argstr.c_str(), "%c", &(filer->type));
|
||||||
|
submit(226, "OK");
|
||||||
|
} else if (cmd == "PASV") {
|
||||||
|
submit(502, "Command not implemented");
|
||||||
|
} else if (cmd == "PORT") {
|
||||||
|
data_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
unsigned char act_port[2];
|
||||||
|
int act_ip[4], port_dec;
|
||||||
|
char ip_decimal[40];
|
||||||
|
sscanf(argstr.c_str(), "%d,%d,%d,%d,%d,%d", &act_ip[0], &act_ip[1], &act_ip[2], &act_ip[3], (int*)&act_port[0], (int*)&act_port[1]);
|
||||||
|
sprintf(ip_decimal, "%d.%d.%d.%d", act_ip[0], act_ip[1], act_ip[2], act_ip[3]);
|
||||||
|
port_dec = act_port[0]*256+act_port[1];
|
||||||
|
printf("[d] D(%i) Initialized data transport: %s:%d\n", control_sock, ip_decimal, port_dec);
|
||||||
|
|
||||||
|
data_address.sin_family = AF_INET;
|
||||||
|
data_address.sin_addr.s_addr = inet_addr(ip_decimal);
|
||||||
|
data_address.sin_port = htons(port_dec);
|
||||||
|
|
||||||
|
if (connect(data_sock, (struct sockaddr *)&data_address, sizeof(data_address)) != 0) {
|
||||||
|
close(data_sock);
|
||||||
|
data_sock = 0;
|
||||||
|
submit(425, "Unknown Error");
|
||||||
|
} else {
|
||||||
|
state = 2;
|
||||||
|
submit(200, "OK");
|
||||||
|
}
|
||||||
|
} else if (cmd == "SIZE") {
|
||||||
|
int ret = filer->fileSize(argstr);
|
||||||
|
if (ret >= 0) {
|
||||||
|
submit(213, std::to_string(ret));
|
||||||
|
} else {
|
||||||
|
submit(550, "Access Denied "+std::to_string(ret));
|
||||||
|
}
|
||||||
|
} else if (cmd == "FEAT") {
|
||||||
|
submit(211, ":\r\n211 END");
|
||||||
|
} else if (cmd == "NOOP") {
|
||||||
|
submit(226, "OK");
|
||||||
|
} else if (cmd == "DELE" || cmd == "RMD") {
|
||||||
|
int ret = filer->deleteFile(argstr);
|
||||||
|
if (ret == 0) {
|
||||||
|
submit(226, "OK");
|
||||||
|
} else {
|
||||||
|
submit(550, "Access Denied "+std::to_string(ret));
|
||||||
|
}
|
||||||
|
} else if (state == 2) {
|
||||||
|
if (data_sock <= 0) return -1;
|
||||||
|
if (cmd == "LIST" || cmd == "NLST") {
|
||||||
|
submit(150, "Transferring");
|
||||||
|
|
||||||
|
std::string output = filer->list();
|
||||||
|
char out[output.size()] = {0};
|
||||||
|
strncpy(out, output.c_str(), output.size());
|
||||||
|
data_submit(out, output.size());
|
||||||
|
|
||||||
|
submit(226, "OK");
|
||||||
|
} else if (cmd == "RETR") {
|
||||||
|
struct file_data fd = filer->readFile(argstr);
|
||||||
|
if (fd.ecode == 0) {
|
||||||
|
submit(150, "Transferring");
|
||||||
|
data_submit(fd.data, fd.size);
|
||||||
|
} else {
|
||||||
|
if (fd.ecode == -3) submit(550, "I refuse to transmit in ASCII mode!");
|
||||||
|
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") {
|
||||||
|
unsigned char inbuf[BUFFERSIZE] = {0};
|
||||||
|
submit(150, "Transferring");
|
||||||
|
int psize;
|
||||||
|
// Write nothing to file to make sure it exists.
|
||||||
|
// Also so I don't have to write a more complex writer.
|
||||||
|
int ret = filer->writeFile(argstr, inbuf, 0);
|
||||||
|
while ((psize = recv(data_sock, inbuf, sizeof(inbuf), 0)) > 0) {
|
||||||
|
ret = filer->appendFile(argstr, inbuf, psize);
|
||||||
|
if (ret < 0) {
|
||||||
|
submit(550, "Access Denied "+std::to_string(ret));
|
||||||
|
data_close();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < BUFFERSIZE; i++) inbuf[i] = 0;
|
||||||
|
}
|
||||||
|
submit(226, "OK");
|
||||||
|
}
|
||||||
|
if (data_close() != 0) {
|
||||||
|
printf("[!] Data Sock returned %i instead of 0! Not closed!!!\n", data_sock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
submit(502, "Command not implemented");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state = 0; // If we reach here, force state to zero just in case.
|
||||||
|
if (cmd == "USER") {
|
||||||
|
name = argstr;
|
||||||
|
submit(331, "Password required");
|
||||||
|
} else if (cmd == "PASS") {
|
||||||
|
if (argstr == "123") {
|
||||||
|
printf("[d] (%i) logged in as '%s'\n", control_sock, name.c_str());
|
||||||
|
// We can now set the root safely (I hope).
|
||||||
|
filer->setRoot("/data/ftp-test/"+name);
|
||||||
|
state = 1;
|
||||||
|
submit(230, "Login successful");
|
||||||
|
} else {
|
||||||
|
submit(530, "Authentication refused.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (cmd == "AUTH") {
|
||||||
|
submit(502, "Command not implemented");
|
||||||
|
} else {
|
||||||
|
submit(332, "Login Required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int submit(int code, std::string msg) {
|
||||||
|
std::string out = std::to_string(code)+" "+msg+"\r\n";
|
||||||
|
int bytes = send(control_sock, out.c_str(), out.size(), 0);
|
||||||
|
if (bytes < 0) {
|
||||||
|
printf("[d] C(%i) !< %i %s\n", control_sock, code, msg.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("[d] C(%i) << %i %s\n", control_sock, code, msg.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int data_submit(char* out, int size) {
|
||||||
|
int bytes = send(data_sock, out, size, 0);
|
||||||
|
if (bytes < 0) {
|
||||||
|
printf("[d] D(%i) !< %i\n", data_sock, size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("[d] D(%i) << %i\n", data_sock, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int data_close() {
|
||||||
|
printf("[d] D(%i) Closing...\n", data_sock);
|
||||||
|
close(data_sock);
|
||||||
|
data_sock = 0;
|
||||||
|
state = 1;
|
||||||
|
return data_sock; // Should be 0 if successful.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
Filer* filer = new Filer();
|
||||||
|
|
||||||
|
int data_sock;
|
||||||
|
struct sockaddr_in data_address;
|
||||||
|
};
|
||||||
233
src/filer.cpp
Normal file
233
src/filer.cpp
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* === THE FILER ===
|
||||||
|
* This class and its structs handle file operations for
|
||||||
|
* specified users. Its goal is an easy, consistent, and
|
||||||
|
* modular class to simply call on from inside Client.
|
||||||
|
*
|
||||||
|
* A Filer object has its defined root that it should never
|
||||||
|
* break out of. If it does, best hope we aren't running as
|
||||||
|
* root.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* == STATUS CODES ==
|
||||||
|
*
|
||||||
|
* -3 - Uhhhhhh
|
||||||
|
* -2 - Invalid Permissions
|
||||||
|
* -1 - File Not Found
|
||||||
|
* 0 - Generic Success
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct file_data {
|
||||||
|
char* data;
|
||||||
|
int size;
|
||||||
|
int ecode;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class Filer {
|
||||||
|
public:
|
||||||
|
fs::path cwd;
|
||||||
|
char type = 'A';
|
||||||
|
|
||||||
|
Filer() {};
|
||||||
|
|
||||||
|
int traverse(std::string dir) {
|
||||||
|
fs::path ndir = fs::weakly_canonical(cwd / dir);
|
||||||
|
fs::path fdir = fullPath(dir);
|
||||||
|
printf("[i] Traversing: %s\n", ndir.c_str());
|
||||||
|
if (fdir.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else if (!fs::exists(fdir) || !fs::is_directory(fdir)) return -1;
|
||||||
|
else {
|
||||||
|
cwd = ndir;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int setRoot(std::string _root) {
|
||||||
|
fs::path froot = fs::weakly_canonical(_root);
|
||||||
|
printf("[i] Setting root: %s\n", froot.c_str());
|
||||||
|
if (!fs::exists(froot)) fs::create_directory(froot);
|
||||||
|
root = fs::absolute(froot);
|
||||||
|
cwd = "/";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int createDirectory(std::string dir) {
|
||||||
|
fs::path ndir = fs::weakly_canonical(cwd / dir);
|
||||||
|
fs::path fdir = fullPath(dir);
|
||||||
|
if (fdir.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else if (fs::exists(fdir)) return -1;
|
||||||
|
else {
|
||||||
|
fs::create_directory(fdir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path fullPath() {
|
||||||
|
return fs::weakly_canonical(root.string()+"/"+cwd.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path fullPath(std::string in) {
|
||||||
|
return fs::weakly_canonical(root.string()+"/"+(cwd / in).string());
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize(std::string name) {
|
||||||
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
|
fs::path ffile = fullPath(name);
|
||||||
|
printf("[i] Retreiving filesize: %s\n", nfile.c_str());
|
||||||
|
if (ffile.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else if (!fs::exists(ffile)) return -1;
|
||||||
|
else if (type == 'A') return -3;
|
||||||
|
else {
|
||||||
|
std::ifstream infile(ffile, std::ios::in|std::ios::binary|std::ios::ate);
|
||||||
|
if (infile.is_open()) {
|
||||||
|
return infile.tellg();
|
||||||
|
} else return -2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteFile(std::string name) {
|
||||||
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
|
fs::path ffile = fullPath(name);
|
||||||
|
printf("[i] Deleting file: %s\n", nfile.c_str());
|
||||||
|
if (ffile.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else if (!fs::exists(ffile)) return -1;
|
||||||
|
else {
|
||||||
|
if (fs::is_directory(ffile) && !fs::is_empty(ffile)) return -5;
|
||||||
|
else return fs::remove(ffile)?0:-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_data readFile(std::string name) {
|
||||||
|
struct file_data fd;
|
||||||
|
|
||||||
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
|
fs::path ffile = fullPath(name);
|
||||||
|
printf("[i] Retreiving file: %s\n", nfile.c_str());
|
||||||
|
if (ffile.string().rfind(root.string(), 0) != 0) fd.ecode = -2;
|
||||||
|
else if (!fs::exists(ffile)) fd.ecode = -1;
|
||||||
|
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);
|
||||||
|
if (infile.is_open()) {
|
||||||
|
fd.size = infile.tellg();
|
||||||
|
fd.data = new char[fd.size];
|
||||||
|
infile.seekg(0, std::ios::beg);
|
||||||
|
infile.read(fd.data, fd.size);
|
||||||
|
infile.close();
|
||||||
|
fd.ecode = 0;
|
||||||
|
} else fd.ecode = -2;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yes, there are two separate functions for essentially the same thing
|
||||||
|
// but with a different flag. Yes, I could've easily combined the two.
|
||||||
|
// No, I don't wanna.
|
||||||
|
int writeFile(std::string name, unsigned char* data, int size) {
|
||||||
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
|
fs::path ffile = fullPath(name);
|
||||||
|
printf("[i] Storing file: %s\n", nfile.c_str());
|
||||||
|
if (ffile.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else {
|
||||||
|
std::ofstream outfile(ffile, std::ios::out|std::ios::binary);
|
||||||
|
outfile.write((char *)data, size);
|
||||||
|
outfile.close();
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int appendFile(std::string name, unsigned char* data, int size) {
|
||||||
|
fs::path nfile = fs::weakly_canonical(cwd / name);
|
||||||
|
fs::path ffile = fullPath(name);
|
||||||
|
if (ffile.string().rfind(root.string(), 0) != 0) return -2;
|
||||||
|
else {
|
||||||
|
std::ofstream outfile(ffile, std::ios::out|std::ios::binary|std::ios::app);
|
||||||
|
outfile.write((char *)data, size);
|
||||||
|
outfile.close();
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets a list of files and folders within current working directory.
|
||||||
|
// Outputs in the format of 'ls -lA'.
|
||||||
|
std::string list() {
|
||||||
|
std::ostringstream listStream;
|
||||||
|
// Not checking for pwd existence. If it doesn't exist and we
|
||||||
|
// got this far, we fucked up anyway.
|
||||||
|
for(fs::directory_entry const& p : fs::directory_iterator(fullPath())) {
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
// Stole part of this from here:
|
||||||
|
// https://github.com/Siim/ftp/blob/master/handles.c#L154
|
||||||
|
// It's amazing how simple shit is missing from std::filesystem
|
||||||
|
// Thanks boost!
|
||||||
|
struct stat fstat;
|
||||||
|
struct tm *time;
|
||||||
|
time_t rawtime;
|
||||||
|
char timebuff[80];
|
||||||
|
|
||||||
|
if (stat(p.path().c_str(), &fstat) == -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert time_t to tm struct */
|
||||||
|
rawtime = fstat.st_mtime;
|
||||||
|
time = localtime(&rawtime);
|
||||||
|
strftime(timebuff, 80, "%b %d %H:%M", time);
|
||||||
|
|
||||||
|
// God should've smitten me before I wrote such attrocities.
|
||||||
|
fs::perms fperms = fs::status(p).permissions();
|
||||||
|
asprintf(
|
||||||
|
&line,
|
||||||
|
"%c%c%c%c%c%c%c%c%c%c %u %4u %4u %12u %s %s\r\n",
|
||||||
|
p.is_directory()?'d':'-',
|
||||||
|
(fperms & fs::perms::owner_read) != fs::perms::none?'r':'-',
|
||||||
|
(fperms & fs::perms::owner_write) != fs::perms::none?'w':'-',
|
||||||
|
(fperms & fs::perms::owner_exec) != fs::perms::none?'x':'-',
|
||||||
|
(fperms & fs::perms::group_read) != fs::perms::none?'r':'-',
|
||||||
|
(fperms & fs::perms::group_write) != fs::perms::none?'w':'-',
|
||||||
|
(fperms & fs::perms::group_exec) != fs::perms::none?'x':'-',
|
||||||
|
(fperms & fs::perms::others_read) != fs::perms::none?'r':'-',
|
||||||
|
(fperms & fs::perms::others_write) != fs::perms::none?'w':'-',
|
||||||
|
(fperms & fs::perms::others_exec) != fs::perms::none?'x':'-',
|
||||||
|
fs::hard_link_count(p),
|
||||||
|
fstat.st_uid,
|
||||||
|
fstat.st_gid,
|
||||||
|
fstat.st_size,
|
||||||
|
timebuff,
|
||||||
|
p.path().filename().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
listStream << std::string(line);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
return listStream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
fs::path root;
|
||||||
|
};
|
||||||
235
src/main.cpp
Normal file
235
src/main.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#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 "server.h"
|
||||||
|
#include "client.cpp"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
// I stole the majority* of this multi-client sock code from IBM
|
||||||
|
// of all places. Had to still modify it for my own uses but
|
||||||
|
// laziness wins again!
|
||||||
|
|
||||||
|
struct pollfd fds[MAXCLIENTS];
|
||||||
|
struct clientfd {
|
||||||
|
Client* client;
|
||||||
|
std::thread thread;
|
||||||
|
bool close = false;
|
||||||
|
} fdc[MAXCLIENTS];
|
||||||
|
|
||||||
|
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.
|
||||||
|
while (fcntl(cfd->client->control_sock, F_GETFD) != -1) {
|
||||||
|
printf("[d] C(%i) Attempting read...\n", cfd->client->control_sock);
|
||||||
|
int rc = recv(cfd->client->control_sock, inbuf, sizeof(inbuf), 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
printf("[d] C(%i) Marking for deletion...\n", cfd->client->control_sock);
|
||||||
|
cfd->close = true;
|
||||||
|
cfd->thread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc , char *argv[]) {
|
||||||
|
int opt = 1,
|
||||||
|
master_socket = -1,
|
||||||
|
newsock = -1,
|
||||||
|
nfds = 1,
|
||||||
|
current_size = 0;
|
||||||
|
|
||||||
|
char inbuf[BUFFERSIZE];
|
||||||
|
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(CONTROL_PORT);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
printf("[i] Server started.\n");
|
||||||
|
while (run) {
|
||||||
|
int pc = poll(fds, nfds, -1);
|
||||||
|
|
||||||
|
if (pc < 0) {
|
||||||
|
perror("poll() failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc == 0) {
|
||||||
|
printf("poll() timed out\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_size = nfds;
|
||||||
|
for (int i = 0; i < current_size; i++) {
|
||||||
|
inbuf[0] = '\0';
|
||||||
|
|
||||||
|
if(fds[i].revents == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(fds[i].revents != POLLIN) {
|
||||||
|
printf("[!] C(%i) Error! revents = %d\n", 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[d] C(%i) Accepted client%i\n", newsock);
|
||||||
|
// If we assign thead if it is still attached,
|
||||||
|
// we suffer greatly, and by we I mean me.
|
||||||
|
if (fdc[nfds].thread.joinable()) {
|
||||||
|
printf("[!] C(%i) Thread still joinable! Detaching...\n", newsock);
|
||||||
|
// Pray the thread will end itself.
|
||||||
|
fdc[nfds].thread.detach();
|
||||||
|
}
|
||||||
|
fds[nfds].fd = newsock;
|
||||||
|
fds[nfds].events = POLLIN;
|
||||||
|
fdc[nfds].client = new Client(newsock);
|
||||||
|
fdc[nfds].thread = std::thread(runClient, &fdc[nfds]);
|
||||||
|
nfds++;
|
||||||
|
} while (newsock != -1);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
int rc = recv(fds[i].fd, inbuf, sizeof(inbuf), 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
if (errno != EWOULDBLOCK) {
|
||||||
|
perror("recv() failed");
|
||||||
|
fdc[i].close = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0 || fdc[i].client == nullptr) {
|
||||||
|
printf("[d] (%i) closed\n", fds[i].fd);
|
||||||
|
fdc[i].close = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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] (%i) >> '%s' '%s'\n", fds[i].fd, cmd.c_str(), args.c_str());
|
||||||
|
|
||||||
|
if (fdc[i].client->receive(cmd, args) < 0) fdc[i].close = true;
|
||||||
|
inbuf[0] = '\0';
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (fdc[i].close) {
|
||||||
|
conn_close:
|
||||||
|
printf("[d] C(%i) Deleting client...\n", fds[i].fd);
|
||||||
|
close(fds[i].fd);
|
||||||
|
fds[i].fd = -1;
|
||||||
|
if (fdc[i].thread.joinable()) fdc[i].thread.detach();
|
||||||
|
fdc[i].client = nullptr;
|
||||||
|
fdc[i].close = false;
|
||||||
|
compress_array = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compress_array) {
|
||||||
|
compress_array = false;
|
||||||
|
printf("[d] Compressing...\n");
|
||||||
|
for (int i = 0; i < nfds; i++) {
|
||||||
|
if (fds[i].fd == -1) {
|
||||||
|
for(int j = i; j < nfds; j++) {
|
||||||
|
printf("[d] Compressing: id %i to fd %i\n", j, fds[j+1].fd);
|
||||||
|
fds[j].fd = fds[j+1].fd;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
nfds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("[d] Compressing complete!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
184
src/main.cpp.bak
Normal file
184
src/main.cpp.bak
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
#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 <vector>
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include "client.cpp"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
// I stole the majority* of this multi-client sock code from IBM
|
||||||
|
// of all places. Had to still modify it for my own uses but
|
||||||
|
// laziness wins again!
|
||||||
|
|
||||||
|
struct pollfd fds[MAXCLIENTS];
|
||||||
|
Client* fclients[MAXCLIENTS];
|
||||||
|
std::future<bool> fthreads[MAXCLIENTS];
|
||||||
|
|
||||||
|
bool run = true, compress_array = false;
|
||||||
|
|
||||||
|
bool runClient(int sock, Client* client) {
|
||||||
|
char inbuf[BUFFERSIZE];
|
||||||
|
int rc;
|
||||||
|
while (rc = recv(sock, inbuf, sizeof(inbuf), 0)) {
|
||||||
|
if (rc < 0) {
|
||||||
|
if (errno != EWOULDBLOCK) {
|
||||||
|
perror("recv() failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0 || client == nullptr) {
|
||||||
|
printf("[d] (%i) closed\n", 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] (%i) >> '%s' '%s'\n", client->control_sock, cmd.c_str(), args.c_str());
|
||||||
|
|
||||||
|
if (client->receive(cmd, args) < 0) break;
|
||||||
|
inbuf[0] = '\0';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc , char *argv[]) {
|
||||||
|
int opt = 1,
|
||||||
|
master_socket = -1,
|
||||||
|
newsock = -1,
|
||||||
|
nfds = 1,
|
||||||
|
current_size = 0;
|
||||||
|
|
||||||
|
char inbuf[BUFFERSIZE];
|
||||||
|
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(CONTROL_PORT);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
fds[0].fd = master_socket;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
int pc = poll(fds, nfds, -1);
|
||||||
|
|
||||||
|
if (pc < 0) {
|
||||||
|
perror("poll() failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc == 0) {
|
||||||
|
printf("poll() timed out. End program.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_size = nfds;
|
||||||
|
for (int i = 0; i < current_size; i++) {
|
||||||
|
inbuf[0] = '\0';
|
||||||
|
/*
|
||||||
|
if(fds[i].revents == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(fds[i].revents != POLLIN) {
|
||||||
|
printf("[!] Error! revents = %d\n", fds[i].revents);
|
||||||
|
run = false;
|
||||||
|
break;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[d] (%i) initialized\n", newsock);
|
||||||
|
fds[nfds].fd = newsock;
|
||||||
|
fds[nfds].events = POLLIN;
|
||||||
|
Client* newClient = new Client(newsock);
|
||||||
|
fclients[nfds] = newClient;
|
||||||
|
fthreads[nfds] = std::async(runClient, newsock, newClient);
|
||||||
|
nfds++;
|
||||||
|
} while (newsock != -1);
|
||||||
|
} else {
|
||||||
|
if (fthreads[i].wait_for(0s) == std::future_status::ready) {
|
||||||
|
close(fds[i].fd);
|
||||||
|
fds[i].fd = -1;
|
||||||
|
compress_array = true;
|
||||||
|
fclients[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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++) {
|
||||||
|
fds[j].fd = fds[j+1].fd;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
nfds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
13
src/server.h
Normal file
13
src/server.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
// BUNCH OF BULLSHIT
|
||||||
|
#define APPNAME "tunnelFTP (Worlio Bridge)"
|
||||||
|
#define APPVER "0.0"
|
||||||
|
#define CONTROL_PORT 21
|
||||||
|
#define DATA_PORT 20
|
||||||
|
#define BUFFERSIZE 1024*512
|
||||||
|
#define MAXCLIENTS 256
|
||||||
|
#define BASEDIR "/data/ftp-test"
|
||||||
|
|
||||||
|
#endif
|
||||||
216
src/tunnel.cpp.bak
Normal file
216
src/tunnel.cpp.bak
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "tunnel.h"
|
||||||
|
|
||||||
|
int main(int argc , char *argv[]) {
|
||||||
|
int opt = 1;
|
||||||
|
int master_socket,
|
||||||
|
addrlen,
|
||||||
|
new_socket,
|
||||||
|
control_socket[MAXCLIENTS],
|
||||||
|
data_socket[MAXCLIENTS],
|
||||||
|
activity,
|
||||||
|
cs;
|
||||||
|
char* client_pwd[MAXCLIENTS];
|
||||||
|
char* client_user[MAXCLIENTS];
|
||||||
|
int max_sd;
|
||||||
|
struct sockaddr_in ctrl_address;
|
||||||
|
struct sockaddr_in data_address;
|
||||||
|
|
||||||
|
char send_buffer[BUFFERSIZE],
|
||||||
|
receive_buffer[BUFFERSIZE];
|
||||||
|
|
||||||
|
fd_set readfds;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAXCLIENTS; i++) {
|
||||||
|
control_socket[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0) {
|
||||||
|
perror("socket failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) {
|
||||||
|
perror("setsockopt");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl_address.sin_family = AF_INET;
|
||||||
|
ctrl_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
ctrl_address.sin_port = htons(CONTROL_PORT);
|
||||||
|
|
||||||
|
if (bind(master_socket, (struct sockaddr *)&ctrl_address, sizeof(ctrl_address))<0) {
|
||||||
|
perror("bind failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
printf("Listener on port %d\n", CONTROL_PORT);
|
||||||
|
|
||||||
|
if (listen(master_socket, 3) < 0) {
|
||||||
|
perror("listen");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//accept the incoming connection
|
||||||
|
addrlen = sizeof(ctrl_address);
|
||||||
|
puts("Waiting for connections ...");
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(master_socket, &readfds);
|
||||||
|
max_sd = master_socket;
|
||||||
|
|
||||||
|
send_buffer[0] = '\0';
|
||||||
|
receive_buffer[0] = '\0';
|
||||||
|
|
||||||
|
//add child sockets to set
|
||||||
|
for (int i = 0; i < MAXCLIENTS; i++) {
|
||||||
|
//socket descriptor
|
||||||
|
cs = control_socket[i];
|
||||||
|
|
||||||
|
//if valid socket descriptor then add to read list
|
||||||
|
if(cs > 0)
|
||||||
|
FD_SET(cs, &readfds);
|
||||||
|
|
||||||
|
//highest file descriptor number, need it for the select function
|
||||||
|
if(cs > max_sd)
|
||||||
|
max_sd = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
//wait for an activity on one of the sockets , timeout is NULL ,
|
||||||
|
//so wait indefinitely
|
||||||
|
activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if ((activity < 0) && (errno!=EINTR)) {
|
||||||
|
printf("select error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(master_socket, &readfds)) {
|
||||||
|
if ((new_socket = accept(master_socket, (struct sockaddr *)&ctrl_address, (socklen_t*)&ctrl_address))<0) {
|
||||||
|
perror("accept");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//inform user of socket number - used in send and receive commands
|
||||||
|
printf("[DEBUG] New connection: socket fd %d, ip %s, port %d\n", new_socket, inet_ntoa(ctrl_address.sin_addr), ntohs(ctrl_address.sin_port));
|
||||||
|
|
||||||
|
//send new connection greeting message
|
||||||
|
sprintf(send_buffer, "220 %s %s\r\n", APPNAME, APPVER);
|
||||||
|
if(send(new_socket, send_buffer, strlen(send_buffer), 0) < 0)
|
||||||
|
perror("send");
|
||||||
|
|
||||||
|
//add new socket to array of sockets
|
||||||
|
for (int i = 0; i < MAXCLIENTS; i++) {
|
||||||
|
//if position is empty
|
||||||
|
if(control_socket[i] == 0) {
|
||||||
|
control_socket[i] = new_socket;
|
||||||
|
client_pwd[i] = "";
|
||||||
|
printf("Adding client %d to list\n", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("CLIENTS [");
|
||||||
|
for (int i = 0; i < MAXCLIENTS; i++) {
|
||||||
|
if (control_socket[i] == 0)
|
||||||
|
printf(" ");
|
||||||
|
else
|
||||||
|
printf("#");
|
||||||
|
}
|
||||||
|
printf("]\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAXCLIENTS; i++) {
|
||||||
|
cs = control_socket[i];
|
||||||
|
int bytes;
|
||||||
|
send_buffer[0] = '\0';
|
||||||
|
|
||||||
|
if (FD_ISSET(cs, &readfds)) {
|
||||||
|
for (int n = 0; n < BUFFERSIZE; n++) {
|
||||||
|
bytes = recv(cs, &receive_buffer[n], 1, 0);
|
||||||
|
if (bytes <= 0) break;
|
||||||
|
if (receive_buffer[n] == '\n' || receive_buffer[n] == '\r') {
|
||||||
|
receive_buffer[n] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bytes <= 0 || strlen(receive_buffer) <= 0) continue;
|
||||||
|
printf("[DEBUG] Received from client %i: '%s'\r\n", cs, receive_buffer);
|
||||||
|
if (strncmp(receive_buffer,"USER",4)==0) {
|
||||||
|
char name[32];
|
||||||
|
strncpy(name, &receive_buffer[5], strlen(receive_buffer)-5);
|
||||||
|
name[strlen(receive_buffer)-5] = '\0';
|
||||||
|
client_user[i] = name;
|
||||||
|
sprintf(send_buffer, "331 Password required\r\n");
|
||||||
|
} else if (strncmp(receive_buffer,"PASS",4)==0 || strncmp(receive_buffer,"pass",4)==0) {
|
||||||
|
sprintf(send_buffer, "230 Login successful\r\n");
|
||||||
|
} else if (strncmp(receive_buffer,"SYST",4)==0) {
|
||||||
|
sprintf(send_buffer, "215 LINUX\r\n");
|
||||||
|
} else if (strncmp(receive_buffer,"PWD",3)==0) {
|
||||||
|
sprintf(send_buffer, "257 '%s'\r\n", client_pwd[i]);
|
||||||
|
} else if (strncmp(receive_buffer,"CWD",3)==0 || strncmp(receive_buffer,"cwd",3)==0) {
|
||||||
|
char directory[BUFFERSIZE];
|
||||||
|
strncpy(directory, &receive_buffer[4], strlen(receive_buffer)-4);
|
||||||
|
directory[strlen(receive_buffer)-4] = '\0';
|
||||||
|
client_pwd[i] = (char*)directory;
|
||||||
|
sprintf(send_buffer, "%s\r\n", client_pwd[i]);
|
||||||
|
} else if (strncmp(receive_buffer,"TYPE",4)==0) {
|
||||||
|
sprintf(send_buffer, "200 OK\r\n");
|
||||||
|
} else if (strncmp(receive_buffer,"PASV",4)==0) {
|
||||||
|
sprintf(send_buffer, "502\r\n");
|
||||||
|
} else if (strncmp(receive_buffer,"PORT",4)==0) {
|
||||||
|
int ds = data_socket[i];
|
||||||
|
ds = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
//local variables
|
||||||
|
unsigned char act_port[2];
|
||||||
|
int act_ip[4], port_dec;
|
||||||
|
char ip_decimal[40];
|
||||||
|
sscanf(receive_buffer, "PORT %d,%d,%d,%d,%d,%d",&act_ip[0],&act_ip[1],&act_ip[2],&act_ip[3],(int*)&act_port[0],(int*)&act_port[1]);
|
||||||
|
sprintf(ip_decimal, "%d.%d.%d.%d", act_ip[0], act_ip[1], act_ip[2],act_ip[3]);
|
||||||
|
printf("[DEBUG] Active IP of client %i is %s\n", cs, ip_decimal);
|
||||||
|
port_dec = act_port[0]*256+act_port[1];
|
||||||
|
printf("[DEBUG] Active port of client %i is %d\n", cs, port_dec);
|
||||||
|
|
||||||
|
data_address.sin_family = AF_INET;
|
||||||
|
data_address.sin_addr.s_addr = inet_addr(ip_decimal);
|
||||||
|
data_address.sin_port = htons(port_dec);
|
||||||
|
|
||||||
|
if (connect(ds, (struct sockaddr *)&data_address, (int) sizeof(struct sockaddr)) != 0) {
|
||||||
|
sprintf(send_buffer, "425 Unknown Error\r\n");
|
||||||
|
close(ds);
|
||||||
|
} else {
|
||||||
|
sprintf(send_buffer, "200 OK\r\n");
|
||||||
|
}
|
||||||
|
} else if (strncmp(receive_buffer,"QUIT",4)==0 || strncmp(receive_buffer,"quit",4)==0) {
|
||||||
|
sprintf(send_buffer, "221 Goodbye\r\n");
|
||||||
|
send(cs, send_buffer, strlen(send_buffer), 0);
|
||||||
|
getpeername(cs, (struct sockaddr*)&ctrl_address, (socklen_t*)&addrlen);
|
||||||
|
printf("[DEBUG] Client disconnected: ip %s, port %d\n", inet_ntoa(ctrl_address.sin_addr), ntohs(ctrl_address.sin_port));
|
||||||
|
close(cs);
|
||||||
|
control_socket[i] = 0;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
sprintf(send_buffer, "500 Unrecognized command\r\n");
|
||||||
|
}
|
||||||
|
if (strlen(send_buffer)>0) {
|
||||||
|
bytes = send(cs, send_buffer, strlen(send_buffer), 0);
|
||||||
|
if (bytes < 0) continue;
|
||||||
|
char debugSend[BUFFERSIZE];
|
||||||
|
strncpy(debugSend, send_buffer, strlen(send_buffer)-2);
|
||||||
|
debugSend[strlen(send_buffer)-2] = '\0';
|
||||||
|
printf("[DEBUG] Sent to client %i: '%s'\r\n", cs, debugSend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
52
src/util.h
Normal file
52
src/util.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
template <typename Out>
|
||||||
|
void split(const std::string &s, char delim, Out result, int limit) {
|
||||||
|
int it = 0;
|
||||||
|
std::istringstream iss(s);
|
||||||
|
std::string item;
|
||||||
|
while (std::getline(iss, item, delim) && it <= limit) {
|
||||||
|
*result++ = item;
|
||||||
|
if (limit > 0) it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string &s, char delim, int limit) {
|
||||||
|
std::vector<std::string> elems;
|
||||||
|
split(s, delim, std::back_inserter(elems), limit);
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string toLower(std::string str) {
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string toUpper(std::string str) {
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c){ return std::toupper(c); });
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* trim(char *str) {
|
||||||
|
char *end;
|
||||||
|
while(isspace(*str))
|
||||||
|
str++;
|
||||||
|
if(*str == 0)
|
||||||
|
return str;
|
||||||
|
end = str + strnlen(str, 128) - 1;
|
||||||
|
while(end > str && isspace(*end))
|
||||||
|
end--;
|
||||||
|
*(end+1) = '\0';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user