Newer
Older
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/sha.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
SLDaemon::options::options() {
this->port = 0;
this->verbose = 3;
}
void SLDaemon::usage(const char *argv0) const {
std::cerr << "usage: "
<< argv0
<< " [-f conffile] [-d scriptdir] [-h] [-v] [-l login]"
<< "[-p pass] [-H host] [-P port] [-V]"
<< std::endl
<< " -f conffile : read and load conf file." << std::endl
<< " -d scriptdir : Scripts directory." << std::endl
<< " -h : Print this usage." << std::endl
<< " -v : Verbose mode." << std::endl
<< " -p secret : Set pass to secret." << std::endl
<< " -H name : Set server host to name." << std::endl
<< " -P number : Set server port to number." << std::endl
<< " -V : Print version and exit." << std::endl;
}
SLDaemon::options *SLDaemon::getoptions(int argc, char *argv[]) const {
options opt, *mopt;
if (argc == 1) {
usage(*argv);
}
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-h")) {
usage(*argv);
}
else if (!strcmp(argv[i], "-v")) {
opt.verbose = 1;
}
else if (!strcmp(argv[i], "-V")) {
std::cout << "sl daemon version : " << VERSION << std::endl;
}
else if (!strcmp(argv[i], "-H")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -h.");
opt.server = string(argv[i]);
}
else if (!strcmp(argv[i], "-P")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -p.");
char *endptr;
opt.port = strtol(argv[i], &endptr, 10);
if (!(*argv[i] != '\0' && *endptr == '\0'))
throw Error(ERR_USAGE, "Unable to convert port to a number.");
}
else if (!strcmp(argv[i], "-f")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -f.");
opt.conffile = string(argv[i]);
}
else if (!strcmp(argv[i], "-d")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -d.");
throw Error(ERR_USAGE, "No enough argument for option -l.");
opt.login = string(argv[i]);
}
else if (!strcmp(argv[i], "-p")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -p.");
*err << "Invalid options : " << string(argv[i]) << ".";
throw *err;
}
}
mopt = new options();
*mopt = opt;
return mopt;
}
SLDaemon::options *SLDaemon::getoptions(const string file) const {
if (file == "")
throw Error(ERR_BADPARAM, "Conf file not implemented");
throw Error(ERR_BADPARAM, "Conf file not implemented");
return NULL;
}
void SLDaemon::applyoptions(const options *opt) {
assert(opt);
if (opt->server != "") options_.server = opt->server;
if (opt->pass != "") options_.pass = opt->pass;
if (opt->verbose != 3) options_.verbose = opt->verbose;
if (opt->conffile != "") options_.conffile = opt->conffile;
if (opt->scriptdir != "") options_.scriptdir = opt->scriptdir;
}
void SLDaemon::check_options() const {
// print info in verbose mode
if (verbose()) {
std::cout << "Server host is : " << options_.server << "." << std::endl;
std::cout << "Server port is : " << options_.port << "." << std::endl;
std::cout << "Daemon login is : " << options_.login << "." << std::endl;
std::cout << "Daemon pass is : " << options_.pass << "." << std::endl;
std::cout << "Daemon scripts directory is : " << options_.scriptdir << "." << std::endl;
std::cout << "Verbose mode : " << verbose() << "." << std::endl;
}
// Check validy of arguement
if (options_.server == "")
throw Error(ERR_BADPARAM, "No server address specified.");
throw Error(ERR_BADPARAM, "No server port specified.");
throw Error(ERR_BADPARAM, "Bad server port number (1 - 65535).");
if (options_.login == "")
throw Error(ERR_BADPARAM, "No login specified.");
throw Error(ERR_BADPARAM, "No scripts directory specified.");
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
void SLDaemon::run() {
char *line;
check_options();
connect();
auth();
while (1) {
line = recvln();
// call right handler
try {
if (!strcmp(line, "EXIT\n"))
cmd_exit();
else if (!strcmp(line, "RELOAD\n"))
cmd_reload();
else if (!strcmp(line, "VERSION\n"))
cmd_version();
else if (!strcmp(line, "CLEAN\n"))
cmd_clean();
else if (!strcmp(line, "LIST\n"))
cmd_list();
else if (!strncmp(line, "EXEC ", 5))
cmd_exec(line);
else if (!strncmp(line, "FILE ", 5))
cmd_file(line);
else if (!strncmp(line, "UPDATE ", 6))
cmd_update(line);
else
proto_violation();
}
catch (const Error &e) {
e.print();
send("!! ");
send(e.message());
}
delete line;
}
}
//******************************************************************************
// network functions
//******************************************************************************
void SLDaemon::connect() {
struct sockaddr_in daddr;
struct hostent *h;
// close existing connexion
if (socket_fs_ == NULL)
disconnect();
// retrieve remote host info
h = gethostbyname(options_.server.c_str());
// create socket
socket_fd_ = socket(PF_INET, SOCK_STREAM, 0);
if (socket_fd_ == -1)
throw Error(ERR_NET, strerror(errno));
daddr.sin_family = AF_INET;
daddr.sin_port = htons(options_.port);
daddr.sin_addr = *((struct in_addr *) h->h_addr);
memset(daddr.sin_zero, '\0', sizeof daddr.sin_zero);
// connect
if (::connect(socket_fd_, (struct sockaddr *) &daddr, sizeof daddr) == -1)
throw Error(ERR_NET, strerror(errno));
// initialize socket stream
if ((socket_fs_ = fdopen(socket_fd_, "r+")) == NULL)
throw Error(ERR_NET, strerror(errno));
void SLDaemon::disconnect() {
if (socket_fs_ == NULL)
if (fclose(socket_fs_))
throw Error(ERR_NET, strerror(errno));
socket_fs_ = NULL;
void SLDaemon::send(const string str, bool buf) {
send(str.c_str(), str.length(), buf);
}
void SLDaemon::send(const char *data, size_t len, bool buf) {
if (fwrite(data, 1, len, socket_fs_) != len)
throw Error(ERR_NET, strerror(errno));
if (!buf && fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
char *SLDaemon::recv(size_t size) {
char *data = new char[size];
if (fread(data, 1, size, socket_fs_) != size) {
delete data;
throw Error(ERR_NET, strerror(errno));
void SLDaemon::recv(size_t size, const string filename) {
char *data = recv(size);
FILE *fs = fopen(filename.c_str(), "w");
if (fs == NULL)
throw Error(ERR_FILE, strerror(errno));
if (fwrite(data, 1, size, fs) != size)
throw Error(ERR_FILE, strerror(errno));
if (fclose(fs))
throw Error(ERR_FILE, strerror(errno));
char *SLDaemon::recvln() {
char *line = new char[MAX_LINE_SIZE];
if (fgets(line, MAX_LINE_SIZE, socket_fs_) == NULL) {
delete line;
throw Error(ERR_FILE, strerror(errno));
}
return line;
}
void SLDaemon::flush() {
if (fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
//******************************************************************************
// protocol functions
//******************************************************************************
void SLDaemon::auth() {
unsigned char md[SHA_DIGEST_LENGTH];
BIO *bmem, *b64;
BUF_MEM *bptr;
SHA1((const unsigned char *) options_.pass.c_str(), options_.pass.length(), md);
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, md, SHA_DIGEST_LENGTH);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
memcpy(buff, bptr->data, bptr->length-1);
buff[bptr->length-1] = 0;
BIO_free_all(b64);
send("USER ");
send(options_.login);
send("\n");
send("PASS ");
delete buff;
char *line = recvln();
if (strcmp(line, "OK\n")) {
delete line;
throw Error(ERR_AUTH, "Authentification failed !");
}
delete line;
void SLDaemon::tee(const string msg) {
if (verbose())
std::cout << msg << std::endl;
send(msg);
send("\n");
}
void SLDaemon::proto_violation() {
std::cerr << "Protocol Violation." << std::endl;
send((string) "Protocol Violation.\n", false);
}
//******************************************************************************
// command functions
//******************************************************************************
std::cout << "EXIT requested." << std::endl;
send("Bye.\n");
exit(ERR_OK);
}
void SLDaemon::cmd_version() {
if (verbose())
std::cout << "VERSION requested." << std::endl;
send(VERSION);
send("\n");
}
void SLDaemon::cmd_exec(const char *line) {
assert(line);
char *p = strchr(line, ' ');
if (p == NULL) {
return;
}
string path = options_.scriptdir + "/" + string(p + 1);
send(">> EXEC " + path + "\n");
if (verbose())
std::cout << ">> EXEC " << path << std::endl;
// fork
pid_t pid = fork();
if (pid == -1)
if (pid > 0) {
int status;
char buf[10];
waitpid(pid, &status, 0);
status = WEXITSTATUS(status);
snprintf(buf, 10, "%d", status);
send((string) ">> EXEC return " + buf + "\n");
}
else if (pid == 0) {
}
if (execl(path.c_str(), path.c_str(), NULL) == -1) {
perror(">> execl");
}
}
}
void SLDaemon::cmd_file(const char *line) {
// get filename
char filename[512]; //FIXME: bad magic size
if (sscanf(line, "FILE %512s\n", filename) != 1) {
proto_violation();
return;
}
string target = options_.scriptdir + "/" + filename;
//get size
int size;
buf = recvln();
ret = sscanf(buf, "SIZE %i\n", &size);
delete buf;
if (ret != 1) {
tee("Invalid size parameter.");
return;
}
//get md5
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
delete buf;
if (ret != 1) {
tee("Invalid md5 parameter.");
return;
}
// show verbose
if (verbose())
std::cout << "FILE transfer requested: "
<< "to=" << target
<< ", size=" << size
<< ", md5=" << md5 << "."
<< std::endl;
//get data
try {
recv(size, target);
}
catch (const Error &err) {
if (err.code() == ERR_FILE) {
tee("Data transfer error.");
return;
}
throw;
}
// check MD5
if (SLDaemon::md5(target) != string(md5)) {
// proceed chown
if (chown(target.c_str(), getuid(), getgid())) {
unlink(target.c_str());
return;
}
// proceed chmod
if (chmod(target.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
unlink(target.c_str());
return;
}
tee("Transfer of " + target + ": OK.");
}
void SLDaemon::cmd_update(const char *line) {
assert(line);
if (verbose())
std::cout << "UPDATE requested." << std::endl;
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
void SLDaemon::cmd_list() {
if (verbose())
std::cout << "LIST requested." << std::endl;
FILE *fls = popen(string("ls -1A " + options_.scriptdir).c_str(), "r");
if (fls == NULL) {
tee("Unable to list " + options_.scriptdir + ".");
return;
}
char buf[255];
size_t len;
try {
while ((len = fread(buf, 1, 255, fls)) > 0)
send(buf, len);
flush();
}
catch (...) {
pclose(fls);
throw;
}
}
void SLDaemon::cmd_clean() {
if (verbose())
std::cout << "CLEAN requested." << std::endl;
}
void SLDaemon::cmd_reload() {
if (verbose())
std::cout << "RELOAD requested." << std::endl;
}
//******************************************************************************
// others functions
//******************************************************************************
string SLDaemon::md5(const string file) const {
MD5_CTX ctx;
FILE *fs;
size_t len;
char buf[512];
char md[MD5_DIGEST_LENGTH];
char digest[MD5_DIGEST_LENGTH * 2 + 1];
if (!MD5_Init(&ctx))
return "";
if ((fs = fopen(file.c_str(), "r")) == NULL)
return "";
while ((len = fread(buf, 1, 512, fs)) > 0)
if (!MD5_Update(&ctx, buf, len))
break;
if (!MD5_Final((unsigned char*)md, &ctx))
return "";
for(len = 0; len < MD5_DIGEST_LENGTH; ++len) {
sprintf(digest + (len * 2), "%02x", (unsigned char) md[len]);
}
digest[MD5_DIGEST_LENGTH * 2] = 0;
return string(digest);