directory to the remote system, then `cd` to that directory and run:
```sh
-./busybox sh start_server.sh
+sh start_server.sh
```
By default the server uses the ports 46515-46550. This can be changed in
directory to the remote system, then `cd` to that directory and run:
```sh
-./perl.com client.pl
+perl client.pl
```
## Monitoring
wget https://github.com/inotify-tools/inotify-tools/archive/refs/tags/4.23.9.0.tar.gz
mv 4.23.9.0.tar.gz inotify-tools-4.23.9.0.tar.gz
wget https://github.com/tstack/lnav/releases/download/v0.11.2/lnav-0.11.2-x86_64-linux-musl.zip
- wget https://github.com/G4Vi/Perl-Dist-APPerl/releases/download/v0.3.0/perl.com
fi
-sha256sum -c ../checksums
+sha256sum -c ../checksums || exit 1
# Compile busybox
# Config file for busybox uses default settings except:
cp busybox-1.36.1/busybox _output/client/
cp inotify-tools-4.23.9.0/src/inotifywait _output/client/
cp pspy64 _output/client/
-cp perl.com _output/client/
cp lnav-0.11.2/lnav _output/server/
-chmod u+x _output/server/*
-chmod u+x _output/client/*
cp ../src/*server* _output/server/
cp ../src/*client* _output/client/
+
+chmod u+x _output/server/*
+chmod u+x _output/client/*
b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314 busybox-1.36.1.tar.bz2
1dfa33f80b6797ce2f6c01f454fd486d30be4dca1b0c5c2ea9ba3c30a5c39855 inotify-tools-4.23.9.0.tar.gz
6515598ca4985ec42daeafbae7eb5c7c6d7e94a11f88666d157b3d363aa391ff lnav-0.11.2-x86_64-linux-musl.zip
-b5997a2683ea993aaa3a0bac08c2172afd94785b22219fddc6876f3740364d59 perl.com
c93f29a5cc1347bdb90e14a12424e6469c8cfea9a20b800bc249755f0043a3bb pspy64
--- /dev/null
+#!/usr/bin/perl
+
+# Clients connect to this server over ssl to request a certificate
+# The provided certificate is signed by a CA ca cert to allow using
+# client certificates for authentication
+
+use strict;
+use warnings;
+use IO::Socket::INET;
+use IO::Socket::SSL;
+use Digest::SHA "sha256";
+use POSIX "strftime";
+
+umask 0077;
+
+$ENV{PATH} = '/bin:/usr/bin';
+
+my %hostnames;
+
+my ($clientname, $clientip);
+open(my $logfh, '>>', '_cert_provider_log.txt');
+sub log_client {
+ my ($message) = @_;
+ $clientname = 'unknown' if ! defined $clientname;
+ $clientip = 'unknown' if ! defined $clientip;
+ $message =~ s/"/\\"/g;
+ my $timestamp = strftime "%Y-%m-%d %H:%M:%S", localtime;
+ my $logmsg = "ts=\"$timestamp\" client=\"$clientname\" ip=\"$clientip\" message=\"$message\"\n";
+ print $logmsg;
+ print $logfh $logmsg;
+}
+
+sub log_client_die {
+ my ($message) = @_;
+ log_client $message;
+ exit 1;
+}
+
+sub find_existing_names {
+ opendir(my $dir, '.');
+ while (readdir($dir)) {
+ my ($name) = $_ =~ /client-([a-zA-Z0-9-]+)-cert.pem/;
+ $hostnames{"$name.c.logs"}++ if defined $name;
+ }
+}
+
+sub generate_ca_cert {
+ print "Generating CA private key and certificate...\n";
+ my $pid = fork();
+ if (!$pid) {
+ open STDOUT, '>', undef;
+ open STDERR, '>', undef;
+ exec 'openssl', 'req', '-x509', '-new', '-noenc', '-newkey', 'rsa:4096',
+ '-sha256', '-days', '3650', '-subj',
+ '/C=AU/ST=none/L=none/O=none/OU=none/CN=ca.logs', '-keyout',
+ 'ca-key.pem', '-out', 'ca-cert.pem' || die "Failed to exec openssl";
+ }
+ waitpid ($pid, 0);
+
+ chmod 0644, 'ca-cert.pem';
+
+ return ('ca-key.pem', 'ca-cert.pem');
+}
+
+sub generate_client_cert {
+ my ($cakey, $cacert, $name, $postfix) = @_;
+
+ $clientname = "${name}${postfix}.logs";
+ my $fn;
+ if ($postfix ne '') {
+ $fn = "client-${name}";
+ } else {
+ $fn = "${name}";
+ }
+
+ if (exists $hostnames{$clientname}) {
+ return (undef, undef);
+ }
+ $hostnames{$clientname}++;
+
+ log_client "Generating private key and certificate for ${clientname}";
+ my $pid = fork();
+ if (!$pid) {
+ # Don't show output
+ open STDOUT, '>', undef;
+ open STDERR, '>', undef;
+ exec 'openssl', 'req', '-x509', '-noenc', '-newkey', 'rsa:4096', '-sha256',
+ '-days', '3650', '-subj', "/C=AU/ST=none/L=none/O=none/OU=none/CN=${clientname}",
+ '-CA', $cacert, '-CAkey', $cakey, '-keyout', "${fn}-key.pem",
+ '-out', "${fn}-cert.pem" || die "Failed to exec openssl";
+ }
+ waitpid($pid, 0);
+
+ return ("${fn}-key.pem", "${fn}-cert.pem", $clientname),
+};
+
+sub fingerprint_cert {
+ my ($cert) = @_;
+ print "$cert : ";
+ my $pid = fork();
+ if (!$pid) {
+ exec 'openssl', 'x509', '-in', $cert, '-noout', '-fingerprint', '-sha256'
+ || die "Failed to exec openssl";
+ }
+ waitpid ($pid, 0);
+
+ return;
+}
+
+sub handle_client {
+ my ($srvkey, $srvcert, $cakey, $cacert, $pw, $client) = @_;
+ my $pid = fork();
+ return if $pid;
+
+ $clientip = $client->peerhost;
+ log_client "Client connected";
+
+ # Upgrade to SSL after fork to not slow down accepting connections
+ IO::Socket::SSL->start_SSL(
+ $client,
+ SSL_server => 1,
+ SSL_cert_file => 'server-cert.pem',
+ SSL_key_file => 'server-key.pem',
+ ) || log_client_die "Failed ssl handshake: $SSL_ERROR";
+
+ my $pw_attempt = "";
+ my $char = "";
+ while ($char ne "\n") {
+ $client->sysread($char, 1) || log_client_die "Client disconnected while providing password";
+ $pw_attempt = "$pw_attempt$char";
+ }
+ chomp $pw_attempt;
+
+ my $sha_pw = sha256($pw);
+ my $sha_pw_attempt = sha256($pw_attempt);
+
+ if ($sha_pw ne $sha_pw_attempt) {
+ log_client_die "Authentication failure";
+ }
+ log_client "Authentication success";
+
+ # Read client name (will be used for the certificate)
+ my $name = "";
+ $char = "";
+ while ($char ne "\n") {
+ $client->sysread($char, 1) || log_client_die "Client disconnected while providing name";
+ if ($char =~ /[a-zA-Z0-9-]/) {
+ $name = "$name$char";
+ }
+ }
+ chomp $name;
+
+ if ($name eq '') {
+ log_client_die "Client provided blank name, no certificate generated";
+ }
+
+ my ($clientkey, $clientcert, $hn) = generate_client_cert($cakey, $cacert, $name, '.c');
+ if (!defined $clientkey || !defined $clientcert) {
+ log_client_die "Client [$clientip] requested existing certificate, no certificate generated";
+ }
+
+ # Send certificate and key to client
+ log_client "Sending client private key and certificate for ${hn}";
+ open(my $ckfh, '<', $clientkey);
+ while (<$ckfh>) {
+ $client->print($_);
+ }
+ open(my $ccfh, '<', $clientcert);
+ while (<$ccfh>) {
+ $client->print($_);
+ }
+ close($ckfh);
+ close($ccfh);
+
+ # Deleting the client's private key since the server doesn't need it
+ unlink($clientkey);
+ exit;
+}
+
+sub help {
+ print STDERR "Usage: $0 port [ca_key ca_certificate [server_key server_certificate]]\n";
+ exit 1;
+}
+
+sub main {
+ my $argcount = scalar @ARGV;
+ if (($argcount != 1 && $argcount != 3 && $argcount != 5) || $ARGV[0] eq '-h') {
+ help;
+ }
+ my ($cakey, $cacert, $srvkey, $srvcert);
+ if (scalar @ARGV == 1) {
+ ($cakey, $cacert) = generate_ca_cert();
+ ($srvkey, $srvcert) = generate_client_cert($cakey, $cacert, 'server', '');
+ } elsif (scalar @ARGV == 1) {
+ $cakey = $ARGV[1];
+ $cacert = $ARGV[2];
+ ($srvkey, $srvcert) = generate_client_cert($cakey, $cacert, 'server', '');
+ } else {
+ $cakey = $ARGV[1];
+ $cacert = $ARGV[2];
+ $srvkey = $ARGV[3];
+ $srvcert = $ARGV[4];
+ }
+
+ print "\n";
+ fingerprint_cert($srvcert);
+
+ print "\nCreating password. Clients will need to provide this to request a certificate\n";
+ print "Set password: ";
+ my $password = <STDIN>;
+ chomp $password;
+
+ find_existing_names;
+
+ my $srv = IO::Socket::INET->new(
+ LocalAddr => '0.0.0.0',
+ LocalPort => $ARGV[0],
+ Listen => 1024,
+ ) || die "Failed to listen: $!";
+
+ $clientname = undef;
+ $clientip = undef;
+ while (1) {
+ my $client = $srv->accept;
+ handle_client($srvkey, $srvcert, $cakey, $cacert, $password, $client);
+ }
+}
+
+main;
#!/usr/bin/env perl
+
+# Client connects to certificate provider to request a client certificate
+# Sends logs and changed files to the server over ssl using the provided
+# client certificate to authenticate
+
use strict;
use warnings;
use IO::Socket::INET;
+use IO::Socket::SSL;
use POSIX "strftime";
+umask 0077;
+
# Change this to the IP of the server
my $server_ip = "127.0.0.1";
my $server_port = 46515;
+my $cert_provider_ip = $server_ip;
+my $cert_provider_port = 46516;
+
+my $server_fingerprint; # Trust on first use if not defined. Will be saved to server_fingerprint.txt
# See what's sent and monitored at the bottom of the script
# Handle SIGINT
my @child_processes;
sub stop_child_processes {
+ return if scalar(@child_processes) == 0;
kill 'INT', @child_processes;
}
$SIG{'INT'} = 'stop_child_processes';
# Register client with server
my ($hostname) = ns_system('./busybox', 'hostname');
-my ($clientName, $clientKey) = register($hostname);
+get_server_fingerprint();
+if (! -f 'client-cert.pem') {
+ printf "Enter password for certificate: ";
+ my $password = <STDIN>;
+ chomp $password;
+ request_certificate($cert_provider_ip, $cert_provider_port, $password, $hostname);
+}
# ------------------------------------------------------------------------------
return @files;
}
+sub request_certificate {
+ my ($cert_provider_ip, $cert_provider_port, $password, $name) = @_;
+ return if (-f 'client-cert.pem');
+
+ my $socket = IO::Socket::SSL->new(
+ PeerAddr => $cert_provider_ip,
+ PeerPort => $cert_provider_port,
+ SSL_fingerprint => $server_fingerprint,
+ ) || die "Failed to connect to certificate provider: $SSL_ERROR";
+ $socket->write("$password\n$name\n");
+ open(my $fh, '>', 'client-cert.pem');
+ my $buffer = "";
+ while (1) {
+ $socket->sysread($buffer, 1024) || last;
+ print $fh $buffer;
+ }
+ $socket->shutdown(SHUT_RD);
+ close $fh;
+}
+
+sub get_server_fingerprint {
+ if (-e 'server_fingerprint.txt') {
+ open(my $fh, '<', 'server_fingerprint.txt');
+ $server_fingerprint = <$fh>;
+ close $fh;
+ return;
+ }
+ my $socket = IO::Socket::SSL->new(
+ PeerAddr => $server_ip,
+ PeerPort => $server_port,
+ SSL_verify_mode => SSL_VERIFY_NONE
+ );
+ $server_fingerprint = $socket->get_fingerprint('sha256');
+ print "Trusting server fingerprint: ", $server_fingerprint, "\n";
+ open(my $fh, '>', 'server_fingerprint.txt');
+ print $fh $server_fingerprint;
+ close $fh;
+}
+
sub connect_to_server {
- my ($port) = @_;
- $port = $server_port if (!defined $port);
- my $socket = IO::Socket::INET->new(
+
+ get_server_fingerprint if !defined $server_fingerprint;
+
+ my $socket = IO::Socket::SSL->new(
PeerAddr => $server_ip,
- PeerPort => $port,
- Proto => 'tcp'
+ PeerPort => $server_port,
+ SSL_cert_file => 'client-cert.pem',
+ SSL_key_file => 'client-cert.pem',
+ SSL_fingerprint => $server_fingerprint,
+ SSL_fast_shutdown => 0,
);
my $wait = 1;
while ((! $socket) && $wait < 16) {
$wait *= 2;
}
if ($wait >= 16) {
- die "Failed to connect to $server_ip:$port : $!";
+ die "Failed to connect to $server_ip:$server_port : $!";
}
$socket->autoflush(1);
return $socket;
}
-sub register {
- my ($hostname) = @_;
- my $socket = connect_to_server;
- $socket->send("register\n");
- $socket->send("$hostname\n");
-
- # Wait for connection to be established, try up to 5 times
- my $response;
- foreach (1..5) {
- sleep $_;
- $socket->recv($response, 80);
- last if ($response =~ m/Key/);
- }
- (my $clientName, my $clientKey) = $response =~ m/Name: (client_[a-zA-Z]+_\d+)\nKey: (\d+)\n/;
-
- if (defined $clientName && defined $clientKey) {
- print_log "Register: success";
- } else {
- print_log "Register: failure";
+sub send_info {
+ my $pid = fork;
+ if ($pid) {
+ push @child_processes, $pid;
+ return;
}
- $socket->close();
- return ($clientName, $clientKey);
-}
-sub login {
my $socket = connect_to_server;
-
- # Wait for connection to be established, try up to 5 times
- my $response;
- $socket->send("login\n");
- $socket->send("$clientName\n");
- $socket->send("$clientKey\n");
- foreach (1..5) {
- sleep $_;
- $socket->recv($response, 80);
- last if ($response =~ m/Auth/);
- }
-
- if ($response =~ m/Auth success/) {
- print_log "Login: success";
- } elsif ($response =~ m/Auth failure/) {
- print_log "Login: failure";
- }
- return $socket;
-}
-
-sub send_info {
- my $socket = login($clientName, $clientKey);
-
my $info = join "", ns_system('./busybox', 'sh', '-c', 'hostname; date; uname -a; cat /etc/os-release; lspci; lsusb; ifconfig');
- $socket->send("info\n");
- $socket->send($info);
- $socket->send("⟃---EOF---⟄\n");
- return;
+ $socket->write("info\n...\n");
+ $socket->write($info);
+
+ $socket->shutdown(SHUT_WR);
+ exit;
}
sub send_log {
# Check that log exists and is readable by current user
exit if (! -e $file || ! -r _);
- my $socket = login($clientName, $clientKey);
-
- # Replace / character with similar-looking character that is valid
- # for filenames. Used to show full path to file
- my $fileName = $file =~ s/\////gr;
+ my $socket = connect_to_server;
# Upload tailed log continuously
- $socket->send("log\n");
- $socket->send("$fileName\n");
+ $socket->write("logs\n");
+ $socket->write("$file\n");
print_log "Log: Uploading $file";
my $tailLog = ns_systemFH('./busybox', './busybox', 'tail', '-F', "$file");
while (<$tailLog>) {
- $socket->send($_);
+ $socket->write($_);
}
print_log "Log: Closing $file";
close($tailLog);
- $socket->send("⟃---EOF---⟄\n");
+
+ $socket->shutdown(SHUT_WR);
exit;
}
return;
}
- my $socket = login($clientName, $clientKey);
-
- # Upload process log continuously
- $socket->send("processes\n");
- print_log "Processes: Started";
- my $commandLog = ns_systemFH('./pspy64', '-f');
- while (<$commandLog>) {
- $socket->send($_);
- }
- print_log "Processes: Finished";
- close($commandLog);
- $socket->send("⟃---EOF---⟄\n");
- exit;
+ my $socket = connect_to_server;
+ ...
}
sub send_command_output {
return;
}
- my $socket = login($clientName, $clientKey);
+ my $socket = connect_to_server;
# Upload command output continously with provided filename
my ($fileName) = $name;
- $socket->send("command\n");
- $socket->send("$fileName\n");
+ $socket->write("cmds\n");
+ $socket->write("$fileName\n");
print_log "Command: Started @command";
my $commandLog = ns_systemFH(@command);
while (<$commandLog>) {
- $socket->send($_);
+ $socket->write($_);
}
print_log "Command: Completed @command";
close($commandLog);
- $socket->send("⟃---EOF---⟄\n");
+
+ $socket->shutdown(SHUT_WR);
exit;
}
# Check that log exists and is readable by current user
exit if (! -e $file || ! -r _);
- # Replace / character with similar-looking character that is valid
- # for filenames. Used to show full path to file
- my $fileName = $file =~ s/\////gr;
- my ($fileHash) = ns_system('./busybox', 'md5sum', "$file");
- chomp $fileName; chomp $fileHash;
- ($fileHash) = $fileHash =~ m/([0-9a-f]+)/;
-
- my $socket = login($clientName, $clientKey);
+ my $socket = connect_to_server;
# Send filename and hash to server, wait for a response with the port to
# upload the file to
- $socket->send("file\n");
- $socket->send("$fileName\n");
- $socket->send("$fileHash\n");
- $socket->recv(my $ignored, 128);
- my $port = undef;
- my $r;
- my $sleeptime = 2;
- my $attemptcount = 0;
- while (! defined $port) {
- sleep $sleeptime;
- $sleeptime *= 2;
- $sleeptime = 10 if ($sleeptime > 10);
- $socket->recv($r, 128);
- ($port) = $r =~ m/(\d+)/;
- $attemptcount += 1;
- if ($attemptcount >= 5) {
- print_log "File: upload failure ($file)";
- exit;
- }
- }
+ $socket->write("file\n");
+ $socket->write("$file\n");
- # Send file once
- print_log "File: upload port is $port ($file)";
+ ## Send file once
+ print_log "File: Uploading $file";
open(my $fileFH, '<', "$file") || die "Failed to open $file";
- my $fileSocket = connect_to_server $port;
while (<$fileFH>) {
- $fileSocket->send($_);
+ $socket->write($_);
}
+ print_log "File: Closing $file";
close($fileFH);
- close($fileSocket);
+ $socket->flush;
- # Server checks that the uploaded hash matches and informs on error
- # No retry is attempted on failed upload
- $socket->recv(my $response, 128);
- if ($response =~ m/Transfer success/) {
- print_log "File: upload success ($file)";
- } else {
- print_log "File: upload failure ($file)";
- }
+ $socket->shutdown(SHUT_WR);
exit;
}
--- /dev/null
+#!/usr/bin/perl
+
+# Client connects to this server over ssl and sends logs, files, and command
+# output which are stored under the following directories according to their
+# type:
+# clients
+# └── development.c.logs
+# ├── _clientinfo.txt
+# ├── _serverlog
+# ├── commands
+# ├── files
+# └── logs
+
+use strict;
+use warnings;
+use IO::Socket::INET;
+use IO::Socket::SSL;
+use POSIX "strftime";
+
+my $perl_binary = '/usr/bin/perl';
+
+umask 0077;
+
+binmode(STDOUT, ":encoding(UTF-8)");
+
+my ($clientname, $clientip);
+my $clientlogfh;
+sub log_client {
+ my ($message) = @_;
+ $clientname = 'unknown' if ! defined $clientname;
+ $clientip = 'unknown' if ! defined $clientip;
+ $message =~ s/"/\\"/g;
+ my $timestamp = strftime "%Y-%m-%d %H:%M:%S", localtime;
+ my $logmsg = "ts=\"$timestamp\" client=\"$clientname\" ip=\"$clientip\" message=\"$message\"\n";
+ print $logmsg;
+ print $clientlogfh $logmsg if defined $clientlogfh;
+}
+
+sub log_client_die {
+ my ($message) = @_;
+ log_client $message;
+ exit 1;
+}
+
+sub write_to_file {
+ my ($client, $filetype, $filename) = @_;
+
+ # Add suffix if file already exists
+ if ($filetype eq 'file' || $filetype eq 'info') {
+ if (-e $filename) {
+ my $suffix = 1;
+ while (-e "${filename}_${suffix}") {
+ ++$suffix;
+ }
+ $filename = "${filename}_${suffix}";
+ }
+ }
+
+ log_client "Started writing to $filename";
+
+ open(my $fh, '>>', $filename);
+ select $fh;
+ $| = 1; # flush stream automatically
+ select STDOUT;
+
+ my $char = "";
+ while (1) {
+ $client->sysread($char, 1024) || last;
+ print $fh $char;
+ }
+ log_client "Finished writing to $filename";
+ $fh->flush;
+ close($fh);
+ $client->shutdown(SHUT_RD);
+ exit;
+}
+
+sub handle_client {
+ my ($client) = @_;
+ if (fork()) {
+ $client->close;
+ return;
+ }
+
+ # Upgrade to SSL after fork to not slow down accepting connections
+ IO::Socket::SSL->start_SSL(
+ $client,
+ SSL_server => 1,
+ SSL_cert_file => 'server-cert.pem',
+ SSL_key_file => 'server-key.pem',
+ SSL_verify_mode => SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ SSL_ca_file => 'ca-cert.pem',
+ SSL_fast_shutdown => 0,
+ ) || log_client_die "Failed ssl handshake: $SSL_ERROR";
+
+ $clientname = $client->peer_certificate('commonName');
+ $clientip = $client->peerhost;
+
+ my $dirname = "clients/$clientname";
+
+ # Create skeleton directories if not present
+ if (! -d $dirname) {
+ mkdir($dirname, 0700);
+ mkdir("$dirname/clients", 0700);
+ mkdir("$dirname/files", 0700);
+ mkdir("$dirname/commands", 0700);
+ }
+ open($clientlogfh, '>>:encoding(UTF-8)', "$dirname/_serverlog");
+ select $clientlogfh;
+ $| = 1; # flush stream automatically
+ select STDOUT;
+
+ log_client 'connected';
+
+ # Read source of data, choose filename prefix accordingly
+ my $filename;
+ my $filetype;
+ my $buffer = "";
+ $client->read($buffer, 5);
+ $buffer =~ s/\n//g;
+
+ if ($buffer eq 'info') {
+ $filetype = 'info';
+ $filename = "$dirname/_clientinfo.txt";
+ } elsif ($buffer eq 'logs') {
+ $filetype = 'logs';
+ $filename = "$dirname/logs/L__";
+ } elsif ($buffer eq 'cmds') {
+ $filetype = 'cmds';
+ $filename = "$dirname/commands/C__";
+ } elsif ($buffer eq 'file') {
+ $filetype = 'file';
+ $filename = "$dirname/files/F__";
+ } else {
+ log_client "provided invalid filetype ($buffer), ignoring";
+ exit;
+ }
+
+ # Read name for logfile, strip illegal characters for windows filenames
+ my $name = "";
+ my $char = "";
+ while ($char ne "\n") {
+ $client->read($char, 1) || log_client_die 'disconnected';
+ if ($char =~ /[^<>:"\/\\|?*]/) {
+ $name = "$name$char";
+ } else {
+ if ($char ne '/') {
+ # Use unicode replacement character
+ $name = "$name\N{REPLACEMENT CHARACTER}";
+ } else {
+ # Use unicode lookalike for /
+ $name = "$name\N{BIG SOLIDUS}";
+ }
+ }
+ }
+ chomp $name;
+
+ if ($name eq '') {
+ log_client 'provided blank filename, ignoring';
+ exit;
+ }
+
+ if ($filetype ne 'info') {
+ $filename = "$filename$name";
+ }
+
+ write_to_file($client, $filetype, $filename);
+
+ print $buffer;
+}
+
+sub help {
+ print STDERR "Usage: $0 port [-c port [-s]]\n";
+ print STDERR "\t-c port\tStart cert-server on port\n";
+ print STDERR "\t-s\tUse saved certificates for cert-server if they exist\n";
+ exit 1;
+}
+
+sub main {
+ my $argcount = scalar @ARGV;
+ if (($argcount != 1 && $argcount != 3 && $argcount != 4)
+ || $ARGV[0] eq '-h'
+ || ($argcount >= 3 && $ARGV[1] ne '-c')
+ || ($argcount == 4 && $ARGV[3] ne '-s')) {
+ help;
+ }
+
+ if ($argcount >= 3) {
+ my @cert_server_args = ('../cert-server.pl', $ARGV[2]);
+ if ($argcount == 4 && (-f 'ca-key.pem' && -f 'ca-cert.pem')) {
+ push @cert_server_args, 'ca-key.pem', 'ca-cert.pem';
+ if (-f 'server-key.pem' && -f 'server-cert.pem') {
+ push @cert_server_args, 'server-key.pem', 'server-cert.pem';
+ }
+ }
+ if (!fork()) {
+ exec $perl_binary, @cert_server_args;
+ }
+
+ while (!(-f 'server-key.pem' && -f 'server-cert.pem')) {
+ sleep 1;
+ }
+ }
+
+ mkdir('clients', 0700);
+
+ my $srv = IO::Socket::INET->new(
+ LocalAddr => '0.0.0.0',
+ LocalPort => $ARGV[0],
+ Listen => 16384,
+ ) || die "Failed to listen: $!";
+
+ print "Server is listening for clients\n";
+ while (1) {
+ my $client = $srv->accept;
+ handle_client($client);
+ $client->close();
+ }
+}
+
+main;
+++ /dev/null
-#!./busybox sh
-
-if [ $# -ne 2 ]; then
- echo "Usage: $0 file_port_start file_port_num"
- exit 1
-fi
-
-FILE_PORT_START=$1
-FILE_PORT_NUM=$2
-
-SRVDIR=$(pwd)
-
-verify_input() {
- case "$@" in
- "") echo "Input error"
- exit
- ;;
- *"/"*) echo "Input error"
- exit
- ;;
- esac
-}
-
-write_to_file() {
- if [ -n "$1" ]; then
- touch "$1"
- while IFS= read -r INPUT; do
- if [ "$INPUT" != "⟃---EOF---⟄" ]; then
- printf "%s" "$INPUT" >> "$1"
- else
- break
- fi
- done
- fi
-}
-
-print_status() {
- echo "Acknowledged"
- "$@"
- echo "Done."
-}
-
-
-# -----------------------------------------------------------------------------
-# Identify/authenticate client
-
-read -r COMMAND
-echo "$COMMAND" >&2
-
-# Existing client
-if [ "$COMMAND" = "login" ]; then
- echo -n "Client name: "
- read -r TMPNAME
- echo -n "Client key: "
- read -r TMPKEY
-
- verify_input "$TMPNAME"
-
- # Check if provided credentials are accurate
- if [ -e "$SRVDIR/clients/$TMPNAME" ]; then
- KEY=$(cat "$SRVDIR/clients/$TMPNAME/_auth-key")
- if [ "$TMPKEY" = "$KEY" ]; then
- CLIENTNAME="$TMPNAME"
- CLIENTDIR="$SRVDIR/clients/$TMPNAME"
- echo "Auth success"
- else
- echo "Auth failure"; exit
- fi
- else
- echo "Auth failure"; exit
- fi
- unset TMPNAME
- unset TMPKEY
-
-# New client
-elif [ "$COMMAND" = "register" ]; then
- echo -n "Hostname: "
- read -r CLIENTHOSTNAME
- # Use shortened hostname and add random suffix to prevent collisions
- CLIENTHOSTNAME=$(echo "$CLIENTHOSTNAME" | sed 's/[^a-zA-Z]//g' | cut -c -16)
- CLIENTNAME="client_${CLIENTHOSTNAME}_$(( RANDOM * 2**30 + RANDOM * 2**15 + RANDOM ))"
- CLIENTDIR="$SRVDIR/clients/$CLIENTNAME"
- CLIENTKEY=$(( RANDOM * 2**30 + RANDOM * 2**15 + RANDOM ))
- mkdir -p "$CLIENTDIR"
- echo "$CLIENTKEY" > "$CLIENTDIR/_auth-key"
- echo "Name: $CLIENTNAME"
- echo "Key: $CLIENTKEY"
- unset CLIENTKEY
-else
- echo "Command not found."
- exit
-fi
-
-cd "$CLIENTDIR"
-
-
-# -----------------------------------------------------------------------------
-# Client communication
-
-while read -r COMMAND; do
- echo "$CLIENTNAME: $COMMAND" >&2
-
- if [ "$COMMAND" = "info" ]; then
- print_status write_to_file _info.txt
-
-
- elif [ "$COMMAND" = "processes" ]; then
- print_status write_to_file _processes.log
-
-
- elif [ "$COMMAND" = "file" ]; then
- echo -n "Filename: "
- read -r TMPFILENAME
- verify_input "F__${TMPFILENAME}"
- echo -n "Hash: "
- read -r TMPHASH
-
- FILENAME="F__${TMPFILENAME}"
-
- # Add suffix if file already exists
- if [ -e "$FILENAME" ]; then
- SUFFIX=1
- while [ -e "${FILENAME}_${SUFFIX}" ]; do
- SUFFIX=$(( SUFFIX + 1 ))
- done
- FILENAME="${FILENAME}_${SUFFIX}"
- fi
-
- # Grab an open port and listen for file. Use netcat since sh can't
- # handle binary data well
- ATTEMPTCOUNT=0
- SLEEPTIME=0
- while true; do
- sleep $(( RANDOM % (SLEEPTIME + 5) + 1 ))
- PORT=$(( (RANDOM * 2 + RANDOM % 2) % FILE_PORT_NUM + FILE_PORT_START ))
- nc -w 7 -l -p "$PORT" > "$FILENAME" 2>/dev/null &
- NC_PID=$!
- # Wait for nc to fail. There seems to be a bug with busybox sh where
- # using the builtin sleep when following the backgrounded nc
- # doesn't sleep, so calling the binary again
- ../../../busybox sleep 1
- if ps -o pid | grep -q -e $NC_PID ; then
- break
- fi
- ATTEMPTCOUNT=$(( ATTEMPTCOUNT + 1 ))
- SLEEPTIME=$(( SLEEPTIME + 2 ))
- if [ $ATTEMPTCOUNT -gt 5 ]; then
- echo "$CLIENTNAME: ($FILENAME) Failed to bind open port for file transfer, giving up" >&2
- echo "[${FILERECVTIME}] File transfer failed: ${FILENAME}" >> _files.log
- exit
- fi
- done
- echo "$PORT"
- wait $NC_PID
-
- FILERECVTIME="$(date '+%Y-%m-%d %H:%M:%S')"
- echo "[${FILERECVTIME}] File received: ${FILENAME}" >> _files.log
-
- HASH=$(md5sum "$FILENAME" | cut -d' ' -f1)
- if [ "$HASH" != "$TMPHASH" ]; then
- echo "Checksum error"
- else
- echo "Transfer success"
- fi
-
- unset TMPFILENAME
-
-
- elif [ "$COMMAND" = "log" ]; then
- echo -n "Filename: "
- read -r TMPFILENAME
- verify_input "L__${TMPFILENAME}"
- print_status write_to_file "L__${TMPFILENAME}"
- unset TMPFILENAME
-
-
- elif [ "$COMMAND" = "command" ]; then
- echo -n "Filename: "
- read -r TMPFILENAME
- verify_input "C__${TMPFILENAME}"
- print_status write_to_file "C__${TMPFILENAME}"
- unset TMPFILENAME
-
-
- else
- echo "Command not found."
- fi
-done
#!./busybox sh
SRVPORT=46515
-FILE_PORT_START=46516
-FILE_PORT_NUM=45
+CRTPORT=46516
CWD=$(pwd)
# -----------------------------------------------------------------------------
# Start listening for clients
-mkdir -p "$CWD/srv/clients"
+mkdir -p "$CWD/srv"
cd "$CWD/srv"
-tcpsvd -c 4096 0.0.0.0 "$SRVPORT" ../busybox sh ../server.sh "$FILE_PORT_START" "$FILE_PORT_NUM"
+perl ../server.pl "$SRVPORT" -c "$CRTPORT" -s