Heavily overhauled client command handling and changed motd to use multi-line text.
This commit is contained in:
368
src/client.cpp
368
src/client.cpp
@@ -39,10 +39,6 @@ public:
|
|||||||
submit(230, conf_format(server_name, (std::map<std::string, std::string>){{std::string(APPNAME_VAR),std::string(APPNAME)},{std::string(VERSION_VAR),std::string(VERSION)}}));
|
submit(230, conf_format(server_name, (std::map<std::string, std::string>){{std::string(APPNAME_VAR),std::string(APPNAME)},{std::string(VERSION_VAR),std::string(VERSION)}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addOption(std::string name, bool toggle) {
|
|
||||||
this->options[name] = toggle;
|
|
||||||
}
|
|
||||||
|
|
||||||
int receive(std::string cmd, std::string argstr) {
|
int receive(std::string cmd, std::string argstr) {
|
||||||
if (control_sock <= 0) return 1;
|
if (control_sock <= 0) return 1;
|
||||||
|
|
||||||
@@ -69,97 +65,127 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "AUTH") {
|
if (cmd == "USER") {
|
||||||
|
if (state == FTP_STATE_GUEST) {
|
||||||
|
if (argstr.length() >= sizeof(auth_data->username))
|
||||||
|
throw std::runtime_error("Username too long");
|
||||||
|
std::strncpy(auth_data->username, argstr.c_str(), sizeof(auth_data->username) - 1);
|
||||||
|
auth_data->username[sizeof(auth_data->username) - 1] = '\0';
|
||||||
|
if (auth->isPasswordRequired())
|
||||||
|
submit(331, "Password required");
|
||||||
|
else authedInit();
|
||||||
|
} else submit(503, "Already logged in");
|
||||||
|
} else if (cmd == "PASS") {
|
||||||
|
if (state == FTP_STATE_GUEST) {
|
||||||
|
if (strlen(auth_data->username) > 0) {
|
||||||
|
std::strncpy(auth_data->password, argstr.c_str(), sizeof(auth_data->password) - 1);
|
||||||
|
auth_data->password[sizeof(auth_data->password) - 1] = '\0';
|
||||||
|
if (auth->authenticate(auth_data))
|
||||||
|
authedInit();
|
||||||
|
else {
|
||||||
|
auth_data->username[0] = {};
|
||||||
|
submit(530, "Invalid Credentials.");
|
||||||
|
}
|
||||||
|
} else submit(503, "Bad sequence of commands");
|
||||||
|
} else submit(503, "Already logged in");
|
||||||
|
} else if (cmd == "REIN") {
|
||||||
|
if (state == FTP_STATE_ONDATA) data_close();
|
||||||
|
auth_data = new ClientAuthDetails{};
|
||||||
|
filer = default_filer_factory();
|
||||||
|
// Reset features
|
||||||
|
for (const auto &x : features)
|
||||||
|
features[x.first] = "OFF";
|
||||||
|
state = FTP_STATE_GUEST;
|
||||||
|
submit(220, "Service ready for user");
|
||||||
|
} else if (cmd == "AUTH") {
|
||||||
if (argstr == "TLS" || argstr == "SSL") {
|
if (argstr == "TLS" || argstr == "SSL") {
|
||||||
if (setupControlSSL()) {
|
if (setupControlSSL()) {
|
||||||
submit(234, "AUTH TLS successful");
|
submit(234, "AUTH TLS successful");
|
||||||
is_secure = true;
|
is_secure = true;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else submit(431, "AUTH TLS failed");
|
||||||
submit(431, "AUTH TLS failed");
|
} else submit(504, "AUTH type not supported");
|
||||||
}
|
|
||||||
} else {
|
|
||||||
submit(504, "AUTH type not supported");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else if (cmd == "PBSZ") {
|
} else if (cmd == "PBSZ") {
|
||||||
if (!is_secure) {
|
if (!is_secure) submit(503, "PBSZ not allowed on insecure control connection");
|
||||||
submit(503, "PBSZ not allowed on insecure control connection");
|
else submit(200, "PBSZ=0");
|
||||||
} else {
|
|
||||||
submit(200, "PBSZ=0");
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (cmd == "PROT") {
|
} else if (cmd == "PROT") {
|
||||||
if (!is_secure) {
|
if (!is_secure) submit(503, "PROT not allowed on insecure control connection");
|
||||||
submit(503, "PROT not allowed on insecure control connection");
|
else if (argstr == "P") {
|
||||||
} else if (argstr == "P") {
|
|
||||||
protect_data = true;
|
protect_data = true;
|
||||||
submit(200, "Protection level set to Private");
|
submit(200, "Protection level set to Private");
|
||||||
} else if (argstr == "C") {
|
} else if (argstr == "C") {
|
||||||
protect_data = false;
|
protect_data = false;
|
||||||
submit(200, "Protection level set to Clear");
|
submit(200, "Protection level set to Clear");
|
||||||
} else {
|
} else submit(504, "PROT level not supported");
|
||||||
submit(504, "PROT level not supported");
|
} else if (cmd == "FEAT") {
|
||||||
}
|
submit("211-Extensions supported:");
|
||||||
return 0;
|
for (const auto &s : global_features)
|
||||||
} else if (cmd == "QUIT") {
|
submit(" "+toUpper(s));
|
||||||
state = FTP_STATE_CLOSE;
|
submit(211, "END");
|
||||||
submit(250, "Goodbye!");
|
} else if (cmd == "OPTS") {
|
||||||
shutdown(control_sock, SHUT_RDWR); // Add immediate shutdown
|
std::vector<std::string> args = parseArgs(argstr);
|
||||||
return 1; // Signal thread to terminate
|
if (args.size() > 0) {
|
||||||
} else if (state >= FTP_STATE_AUTHED) {
|
std::string name = toUpper(args[0]);
|
||||||
if (cmd == "SYST") {
|
if (args.size() > 1) {
|
||||||
submit(215, "UNIX Type: L8");
|
if (features.find(name) == features.end())
|
||||||
} else if (cmd == "TYPE") {
|
features[name] = toUpper(args[1]);
|
||||||
|
else submit(451, "Unknown feature "+name);
|
||||||
|
} else {
|
||||||
|
if (features.find(name) == features.end())
|
||||||
|
submit(200, name+" is set to "+features[name]);
|
||||||
|
else submit(501, "Unknown feature "+name);
|
||||||
|
}
|
||||||
|
} else submit(550, "Malformed Request");
|
||||||
|
} else if (cmd == "NOOP") {
|
||||||
|
submit(226, "OK");
|
||||||
|
} else if (cmd == "SYST") {
|
||||||
|
submit(215, "UNIX Type: L8");
|
||||||
|
} else if (cmd == "TYPE") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
char type = 0;
|
char type = 0;
|
||||||
sscanf(argstr.c_str(), "%c", &(type));
|
sscanf(argstr.c_str(), "%c", &(type));
|
||||||
filer->setTransferMode(type);
|
filer->setTransferMode(type);
|
||||||
submit(226, "OK");
|
submit(226, "OK");
|
||||||
} else if (cmd == "OPTS") {
|
} else submit(530, "Not logged in");
|
||||||
std::vector<std::string> args = parseArgs(argstr);
|
} else if (cmd == "PWD") {
|
||||||
if (args.size() > 0) {
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
std::string name = toUpper(args[0]);
|
|
||||||
int rc = false;
|
|
||||||
|
|
||||||
if (args.size() > 1) rc = toggleOption(name, toUpper(args[1])=="ON");
|
|
||||||
else rc = toggleOption(name, true);
|
|
||||||
|
|
||||||
if (rc == 0) submit(200, "OK");
|
|
||||||
else if (rc == 1) submit(451, "Option not enabled or not recognized");
|
|
||||||
else submit(550, "Unknown error");
|
|
||||||
} else submit(550, "Malformed Request");
|
|
||||||
} else if (cmd == "PWD") {
|
|
||||||
submit(257, "'"+filer->getCWD().string()+"'");
|
submit(257, "'"+filer->getCWD().string()+"'");
|
||||||
} else if (cmd == "CWD") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "CWD") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
struct file_data fd = filer->traverse(argstr);
|
struct file_data fd = filer->traverse(argstr);
|
||||||
if (fd.error.code == 0) submit(250, "OK");
|
if (fd.error.code == 0) submit(250, "OK");
|
||||||
else submit(431, std::string(fd.error.msg));
|
else submit(431, std::string(fd.error.msg));
|
||||||
} else if (cmd == "CDUP") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "CDUP") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
struct file_data fd = filer->traverse("..");
|
struct file_data fd = filer->traverse("..");
|
||||||
if (fd.error.code == 0) submit(250, "OK");
|
if (fd.error.code == 0) submit(250, "OK");
|
||||||
else submit(431, std::string(fd.error.msg));
|
else submit(431, std::string(fd.error.msg));
|
||||||
} else if (cmd == "MKD") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "MKD") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
struct file_data fd = filer->createDirectory(argstr);
|
struct file_data fd = filer->createDirectory(argstr);
|
||||||
if (fd.error.code == 0) submit(257, "\""+std::string(fd.path)+"\" directory created");
|
if (fd.error.code == 0) submit(257, "\""+std::string(fd.path)+"\" directory created");
|
||||||
else if (fd.error.code == FilerStatusCodes::FileExists) submit(521, std::string(fd.error.msg));
|
else if (fd.error.code == FilerStatusCodes::FileExists) submit(521, std::string(fd.error.msg));
|
||||||
else submit(550, std::string(fd.error.msg));
|
else submit(550, std::string(fd.error.msg));
|
||||||
} else if (cmd == "SIZE") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "SIZE") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
struct file_data fd = filer->fileSize(argstr);
|
struct file_data fd = filer->fileSize(argstr);
|
||||||
if (fd.error.code == 0) submit(213, std::to_string(fd.size));
|
if (fd.error.code == 0) submit(213, std::to_string(fd.size));
|
||||||
else submit(550, std::string(fd.error.msg));
|
else submit(550, std::string(fd.error.msg));
|
||||||
} else if (cmd == "FEAT") {
|
} else submit(530, "Not logged in");
|
||||||
submit("211-Extensions supported:");
|
} else if (cmd == "DELE" || cmd == "RMD") {
|
||||||
for (const auto &opt : options) {
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
submit(" "+toUpper(opt.first));
|
|
||||||
}
|
|
||||||
submit(211, "END");
|
|
||||||
} else if (cmd == "NOOP") {
|
|
||||||
submit(226, "OK");
|
|
||||||
} else if (cmd == "DELE" || cmd == "RMD") {
|
|
||||||
struct file_data fd = filer->deleteFile(argstr);
|
struct file_data fd = filer->deleteFile(argstr);
|
||||||
if (fd.error.code == 0) submit(250, "OK");
|
if (fd.error.code == 0) submit(250, "OK");
|
||||||
else submit(550, std::string(fd.error.msg));
|
else submit(550, std::string(fd.error.msg));
|
||||||
} else if (cmd == "PASV") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "PASV") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
|
if (state == FTP_STATE_ONDATA) data_close();
|
||||||
if ((data_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
if ((data_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||||
perror("pasv socket() failed");
|
perror("pasv socket() failed");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -233,9 +259,11 @@ public:
|
|||||||
submit(425, "Can't open data connection");
|
submit(425, "Can't open data connection");
|
||||||
data_close();
|
data_close();
|
||||||
}
|
}
|
||||||
} else if (cmd == "RNFR") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "RNFR") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
if (argstr.empty()) {
|
if (argstr.empty()) {
|
||||||
submit(501, "Syntax error in parameters or arguments.");
|
submit(501, "Syntax error in parameters or arguments");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,15 +278,17 @@ public:
|
|||||||
// Store the source path and mark rename as pending
|
// Store the source path and mark rename as pending
|
||||||
rename_from = argstr;
|
rename_from = argstr;
|
||||||
rename_pending = true;
|
rename_pending = true;
|
||||||
submit(350, "Ready for RNTO.");
|
submit(350, "Ready for RNTO");
|
||||||
} else if (cmd == "RNTO") {
|
} else submit(530, "Not logged in");
|
||||||
|
} else if (cmd == "RNTO") {
|
||||||
|
if (state >= FTP_STATE_AUTHED) {
|
||||||
if (!rename_pending) {
|
if (!rename_pending) {
|
||||||
submit(503, "RNFR required first.");
|
submit(503, "RNFR required first");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argstr.empty()) {
|
if (argstr.empty()) {
|
||||||
submit(501, "Syntax error in parameters or arguments.");
|
submit(501, "Syntax error in parameters or arguments");
|
||||||
rename_pending = false;
|
rename_pending = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -267,136 +297,97 @@ public:
|
|||||||
struct file_data fd = filer->renameFile(rename_from, argstr);
|
struct file_data fd = filer->renameFile(rename_from, argstr);
|
||||||
rename_pending = false; // Reset rename state
|
rename_pending = false; // Reset rename state
|
||||||
|
|
||||||
if (fd.error.code == 0) {
|
if (fd.error.code == 0) submit(250, "Rename successful");
|
||||||
submit(250, "Rename successful.");
|
else submit(550, std::string(fd.error.msg));
|
||||||
} else {
|
} else submit(530, "Not logged in");
|
||||||
submit(550, std::string(fd.error.msg));
|
} else if (cmd == "LIST" || cmd == "NLST") {
|
||||||
}
|
if (state == FTP_STATE_ONDATA && data_sock > 0) {
|
||||||
} else if (state == FTP_STATE_ONDATA) {
|
submit(150, "Transferring");
|
||||||
if (data_sock <= 0) return 1;
|
|
||||||
if (cmd == "LIST" || cmd == "NLST") {
|
std::string dirname = "";
|
||||||
|
if (argstr.find_first_of('/') != std::string::npos)
|
||||||
|
dirname = argstr.substr(argstr.find_first_of('/'));
|
||||||
|
struct file_data fd = filer->list(dirname);
|
||||||
|
data_submit(fd.bin, fd.size);
|
||||||
|
|
||||||
|
submit(226, "OK");
|
||||||
|
data_close();
|
||||||
|
} else submit(425, "No data connection");
|
||||||
|
} else if (cmd == "RETR") {
|
||||||
|
if (state == FTP_STATE_ONDATA && data_sock > 0) {
|
||||||
|
struct file_data fd = filer->readFile(argstr);
|
||||||
|
if (fd.error.code == 0 && fd.stream && fd.stream->is_open()) {
|
||||||
submit(150, "Transferring");
|
submit(150, "Transferring");
|
||||||
|
|
||||||
std::string dirname = "";
|
char buffer[8192];
|
||||||
if (argstr.find_first_of('/') != std::string::npos)
|
bool transfer_ok = true;
|
||||||
dirname = argstr.substr(argstr.find_first_of('/'));
|
|
||||||
struct file_data fd = filer->list(dirname);
|
|
||||||
data_submit(fd.bin, fd.size);
|
|
||||||
|
|
||||||
submit(226, "OK");
|
while (!fd.stream->eof()) {
|
||||||
} else if (cmd == "RETR") {
|
fd.stream->read(buffer, sizeof(buffer));
|
||||||
struct file_data fd = filer->readFile(argstr);
|
size_t bytes_read = fd.stream->gcount();
|
||||||
if (fd.error.code == 0 && fd.stream && fd.stream->is_open()) {
|
|
||||||
submit(150, "Transferring");
|
|
||||||
|
|
||||||
char buffer[8192];
|
if (bytes_read > 0) {
|
||||||
bool transfer_ok = true;
|
if (data_submit(buffer, bytes_read) != 0) {
|
||||||
|
submit(426, "Transfer failed");
|
||||||
while (!fd.stream->eof()) {
|
|
||||||
fd.stream->read(buffer, sizeof(buffer));
|
|
||||||
size_t bytes_read = fd.stream->gcount();
|
|
||||||
|
|
||||||
if (bytes_read > 0) {
|
|
||||||
if (data_submit(buffer, bytes_read) != 0) {
|
|
||||||
submit(426, "Transfer failed");
|
|
||||||
transfer_ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd.stream->fail() && !fd.stream->eof()) {
|
|
||||||
submit(426, "Read error");
|
|
||||||
transfer_ok = false;
|
transfer_ok = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transfer_ok) {
|
if (fd.stream->fail() && !fd.stream->eof()) {
|
||||||
submit(226, "OK");
|
submit(426, "Read error");
|
||||||
}
|
transfer_ok = false;
|
||||||
} else {
|
|
||||||
submit(550, std::string(fd.error.msg));
|
|
||||||
}
|
|
||||||
data_close();
|
|
||||||
} else if (cmd == "STOR") {
|
|
||||||
unsigned char inbuf[BUFFERSIZE] = {0};
|
|
||||||
submit(150, "Transferring");
|
|
||||||
int psize;
|
|
||||||
struct file_data fd = filer->writeFile(argstr, inbuf, 0);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (is_secure && protect_data && data_ssl) {
|
|
||||||
psize = SSL_read(data_ssl, inbuf, sizeof(inbuf));
|
|
||||||
if (psize <= 0) {
|
|
||||||
int err = SSL_get_error(data_ssl, psize);
|
|
||||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
psize = recv(data_sock, inbuf, sizeof(inbuf), 0);
|
|
||||||
if (psize <= 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = filer->writeFile(argstr, inbuf, psize, true);
|
|
||||||
if (fd.error.code != 0) {
|
|
||||||
submit(550, "Access Denied: "+std::string(fd.error.msg));
|
|
||||||
data_close();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memset(inbuf, 0, BUFFERSIZE);
|
|
||||||
}
|
}
|
||||||
submit(226, "OK");
|
if (transfer_ok) submit(226, "OK");
|
||||||
} else {
|
} else submit(550, std::string(fd.error.msg));
|
||||||
submit(502, "Command not implemented");
|
|
||||||
}
|
|
||||||
data_close();
|
data_close();
|
||||||
} else {
|
} else submit(425, "No data connection");
|
||||||
submit(502, "Command not implemented");
|
} else if (cmd == "STOR") {
|
||||||
}
|
if (state == FTP_STATE_ONDATA && data_sock > 0) {
|
||||||
} else {
|
unsigned char inbuf[BUFFERSIZE] = {0};
|
||||||
state = FTP_STATE_GUEST; // If we reach here, force state to zero just in case.
|
submit(150, "Transferring");
|
||||||
if (cmd == "USER") {
|
int psize;
|
||||||
if (argstr.length() >= sizeof(auth_data->username)) {
|
struct file_data fd = filer->writeFile(argstr, inbuf, 0);
|
||||||
throw std::runtime_error("Username too long");
|
|
||||||
|
while (true) {
|
||||||
|
if (is_secure && protect_data && data_ssl) {
|
||||||
|
psize = SSL_read(data_ssl, inbuf, sizeof(inbuf));
|
||||||
|
if (psize <= 0) {
|
||||||
|
int err = SSL_get_error(data_ssl, psize);
|
||||||
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
psize = recv(data_sock, inbuf, sizeof(inbuf), 0);
|
||||||
|
if (psize <= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = filer->writeFile(argstr, inbuf, psize, true);
|
||||||
|
if (fd.error.code != 0) {
|
||||||
|
submit(550, "Access Denied: "+std::string(fd.error.msg));
|
||||||
|
data_close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memset(inbuf, 0, BUFFERSIZE);
|
||||||
}
|
}
|
||||||
std::strncpy(auth_data->username, argstr.c_str(), sizeof(auth_data->username) - 1);
|
submit(226, "OK");
|
||||||
auth_data->username[sizeof(auth_data->username) - 1] = '\0';
|
data_close();
|
||||||
if (auth->isPasswordRequired())
|
} else submit(425, "No data connection");
|
||||||
submit(331, "Password required");
|
} else if (cmd == "QUIT") {
|
||||||
else authedInit();
|
state = FTP_STATE_CLOSE;
|
||||||
} else if (cmd == "PASS") {
|
submit(250, "Goodbye!");
|
||||||
std::strncpy(auth_data->password, argstr.c_str(), sizeof(auth_data->password) - 1);
|
shutdown(control_sock, SHUT_RDWR); // Add immediate shutdown
|
||||||
auth_data->password[sizeof(auth_data->password) - 1] = '\0';
|
return 1; // Signal thread to terminate
|
||||||
if (auth->authenticate(auth_data)) authedInit();
|
} else submit(502, "Command not implemented");
|
||||||
else {
|
|
||||||
auth_data->username[0] = {};
|
|
||||||
submit(530, "Invalid Credentials.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
submit(332, "Not Logged In!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int toggleOption(std::string name, bool toggle) {
|
void addFeature(std::string name, std::string value) {
|
||||||
auto feat_it = options.find(name);
|
this->features[name] = value.empty()?"OFF":value;
|
||||||
if (feat_it != options.end()) {
|
|
||||||
feat_it->second = toggle;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getOption(std::string name) {
|
|
||||||
auto feat_it = options.find(name);
|
|
||||||
if (feat_it != options.end()) {
|
|
||||||
return feat_it->second;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int submit(std::string msg) {
|
int submit(std::string msg) {
|
||||||
@@ -445,6 +436,15 @@ public:
|
|||||||
logger->print(LOGLEVEL_DEBUG, "C(%i) << %d %s", control_sock, code, msg.c_str());
|
logger->print(LOGLEVEL_DEBUG, "C(%i) << %d %s", control_sock, code, msg.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void submit(int code, const std::vector<std::string>& msgs) {
|
||||||
|
for(int i = 0; i < msgs.size(); i++){
|
||||||
|
if (i+1 == msgs.size())
|
||||||
|
submit(std::to_string(code) + " " + msgs[i]);
|
||||||
|
else
|
||||||
|
submit(std::to_string(code) + "-" + msgs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int data_submit(char* out, size_t size) {
|
int data_submit(char* out, size_t size) {
|
||||||
size_t total_sent = 0;
|
size_t total_sent = 0;
|
||||||
while (total_sent < size) {
|
while (total_sent < size) {
|
||||||
@@ -514,7 +514,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, bool> options;
|
std::map<std::string, std::string> features;
|
||||||
ClientAuthDetails* auth_data = new ClientAuthDetails{};
|
ClientAuthDetails* auth_data = new ClientAuthDetails{};
|
||||||
Filer* filer = {};
|
Filer* filer = {};
|
||||||
const int opt = 1;
|
const int opt = 1;
|
||||||
@@ -634,6 +634,7 @@ private:
|
|||||||
struct file_data fd;
|
struct file_data fd;
|
||||||
std::string root;
|
std::string root;
|
||||||
std::string home = "/";
|
std::string home = "/";
|
||||||
|
std::vector<std::string> auth_response;
|
||||||
if (auth->isChroot()) {
|
if (auth->isChroot()) {
|
||||||
root = std::string(this->auth_data->home_dir);
|
root = std::string(this->auth_data->home_dir);
|
||||||
logger->print(LOGLEVEL_INFO, "C(%i) Set chrooted root of '%s' to '%s'", control_sock, auth_data->username, root.c_str());
|
logger->print(LOGLEVEL_INFO, "C(%i) Set chrooted root of '%s' to '%s'", control_sock, auth_data->username, root.c_str());
|
||||||
@@ -647,15 +648,16 @@ private:
|
|||||||
((struct file_data)filer->setCWD(home)).error.code == 0
|
((struct file_data)filer->setCWD(home)).error.code == 0
|
||||||
) {
|
) {
|
||||||
state = FTP_STATE_AUTHED;
|
state = FTP_STATE_AUTHED;
|
||||||
submit(230, "Login OK");
|
auth_response.push_back("Login OK");
|
||||||
std::string user_motd = getMotD();
|
std::string user_motd = getMotD();
|
||||||
if (user_motd.size() > 0) {
|
if (user_motd.size() > 0) {
|
||||||
std::stringstream motd_stream(user_motd);
|
std::stringstream motd_stream(user_motd);
|
||||||
std::string line;
|
std::string line;
|
||||||
while(std::getline(motd_stream, line)) {
|
while(std::getline(motd_stream, line)) {
|
||||||
submit(230, line);
|
auth_response.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
submit(230, auth_response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
submit(530, "An error occured setting root and/or cwd");
|
submit(530, "An error occured setting root and/or cwd");
|
||||||
|
|||||||
18
src/main.cpp
18
src/main.cpp
@@ -248,7 +248,9 @@ int main(int argc , char *argv[]) {
|
|||||||
logger->openFileOnLevel(LOGLEVEL_ERROR, config->getValue("logging", "error", mainLogFile).c_str());
|
logger->openFileOnLevel(LOGLEVEL_ERROR, config->getValue("logging", "error", mainLogFile).c_str());
|
||||||
logger->openFileOnLevel(LOGLEVEL_CRITICAL, config->getValue("logging", "critical", mainLogFile).c_str());
|
logger->openFileOnLevel(LOGLEVEL_CRITICAL, config->getValue("logging", "critical", mainLogFile).c_str());
|
||||||
|
|
||||||
if (config->getBool("net", "ssl", false)) {
|
bool ssl_enable = config->getBool("net", "ssl", true);
|
||||||
|
bool ssl_flags = SSL_OP_NO_TICKET;
|
||||||
|
if (ssl_enable) {
|
||||||
std::string cert_file = config->getValue("ssl", "certificate", "cert.pem");
|
std::string cert_file = config->getValue("ssl", "certificate", "cert.pem");
|
||||||
if (cert_file[0] != '/')
|
if (cert_file[0] != '/')
|
||||||
cert_file = concatPath(std::string(CONFIG_DIR), cert_file);
|
cert_file = concatPath(std::string(CONFIG_DIR), cert_file);
|
||||||
@@ -264,7 +266,6 @@ int main(int argc , char *argv[]) {
|
|||||||
// set configured ciphers
|
// set configured ciphers
|
||||||
SSLManager::getInstance().setCiphers(config->getValue("ssl", "ciphers", "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH").c_str());
|
SSLManager::getInstance().setCiphers(config->getValue("ssl", "ciphers", "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH").c_str());
|
||||||
// handle configured flags
|
// handle configured flags
|
||||||
bool ssl_flags = SSL_OP_NO_TICKET;
|
|
||||||
if (!config->getBool("ssl", "ssl_v2", false))
|
if (!config->getBool("ssl", "ssl_v2", false))
|
||||||
ssl_flags+=SSL_OP_NO_SSLv2;
|
ssl_flags+=SSL_OP_NO_SSLv2;
|
||||||
if (!config->getBool("ssl", "ssl_v3", false))
|
if (!config->getBool("ssl", "ssl_v3", false))
|
||||||
@@ -278,6 +279,15 @@ int main(int argc , char *argv[]) {
|
|||||||
if (!config->getBool("ssl", "tls_v1_3", true))
|
if (!config->getBool("ssl", "tls_v1_3", true))
|
||||||
ssl_flags+=SSL_OP_NO_TLSv1_3;
|
ssl_flags+=SSL_OP_NO_TLSv1_3;
|
||||||
|
|
||||||
|
if (!(ssl_flags & SSL_OP_NO_SSLv2) || !(ssl_flags & SSL_OP_NO_SSLv3))
|
||||||
|
global_features.push_back("AUTH SSL");
|
||||||
|
if (!(ssl_flags & SSL_OP_NO_TLSv1) || !(ssl_flags & SSL_OP_NO_TLSv1_1) ||
|
||||||
|
!(ssl_flags & SSL_OP_NO_TLSv1_2) || !(ssl_flags & SSL_OP_NO_TLSv1_3))
|
||||||
|
global_features.push_back("AUTH TLS");
|
||||||
|
|
||||||
|
global_features.push_back("PBSZ");
|
||||||
|
global_features.push_back("PROT");
|
||||||
|
|
||||||
if ((ssl_flags & SSL_OP_NO_SSLv2) && (ssl_flags & SSL_OP_NO_SSLv3) &&
|
if ((ssl_flags & SSL_OP_NO_SSLv2) && (ssl_flags & SSL_OP_NO_SSLv3) &&
|
||||||
(ssl_flags & SSL_OP_NO_TLSv1) && (ssl_flags & SSL_OP_NO_TLSv1_1) &&
|
(ssl_flags & SSL_OP_NO_TLSv1) && (ssl_flags & SSL_OP_NO_TLSv1_1) &&
|
||||||
(ssl_flags & SSL_OP_NO_TLSv1_2) && (ssl_flags & SSL_OP_NO_TLSv1_3))
|
(ssl_flags & SSL_OP_NO_TLSv1_2) && (ssl_flags & SSL_OP_NO_TLSv1_3))
|
||||||
@@ -413,8 +423,8 @@ int main(int argc , char *argv[]) {
|
|||||||
fds[slot].revents = 0;
|
fds[slot].revents = 0;
|
||||||
|
|
||||||
fdc[slot].client = new Client(newsock);
|
fdc[slot].client = new Client(newsock);
|
||||||
fdc[slot].client->addOption("SIZE", true);
|
for (const auto &o : client_options)
|
||||||
fdc[slot].client->addOption("UTF8", config->getBool("features", "utf8", true));
|
fdc[slot].client->addFeature(o, "");
|
||||||
fdc[slot].thread = new std::thread(runClient, &fdc[slot]);
|
fdc[slot].thread = new std::thread(runClient, &fdc[slot]);
|
||||||
fdc[slot].close = false;
|
fdc[slot].close = false;
|
||||||
|
|
||||||
|
|||||||
@@ -25,5 +25,13 @@ Logger* logger;
|
|||||||
static typename PluginTraits<Filer>::CreateFunc default_filer_factory = nullptr;
|
static typename PluginTraits<Filer>::CreateFunc default_filer_factory = nullptr;
|
||||||
bool runServer;
|
bool runServer;
|
||||||
bool runCompression;
|
bool runCompression;
|
||||||
|
std::vector<std::string> global_features = {
|
||||||
|
"SIZE",
|
||||||
|
"UTF8",
|
||||||
|
"PASV"
|
||||||
|
};
|
||||||
|
std::vector<std::string> client_options = {
|
||||||
|
"UTF8"
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user