Passing name of file via socket in perl

Viewed 77

I have 2 scripts written in perl. First one takes a file and send it via socket to server. The server is my second script - and it saves to a file. Server save file as a specified name - fixed in code. How to take the name of sending file, and send it to the server, before sending a file? My code below: Client:

my $socket = IO::Socket::INET->new(
    PeerAddr    =>  $local_host,
    PeerPort    =>  $local_port,
    Proto       => 'tcp',

)or die "Alert!";
my $bandwidth = 1024*5 ; # 5Kb/s - 
open my $fh, '<', "$direc/$my_data"
    or die "couldn't open the file";

  my $buffer ;
  while( sysread($fh, $buffer , $bandwidth) ) {
    print $socket $buffer ;
    sleep(1) ;
  }

  print "Data send.End \n" ;

  close ($fh) ;
  close($socket) ;

My server:

my $my_socket = new IO::Socket::INET(
    LocalHost => $local_host,
    LocalPort => $local_port,
    Proto     => 'tcp',
    Listen    => 5,
    Reuse     => 1
);
die "Couldn't open my_socket $!n " unless $my_socket;
print "You can send the data now \n";
my $accepter = $my_socket->accept();
my $count=0;
#print "$directory.$save_dir/$my_data";
open my $fh, '>', "$direc/$save_dir/$my_data" #my data is the name, and it's "fixed", how to take it from client?
    or die "Couldn't open the file";
while(<$accepter>){
    chomp;
    last if $count++ ==10;
    say $fh $_;

}


print "End \n";
close $fh;
close $my_socket;
1 Answers

Having the server write a filename specified by the client is a security risk. The client could tell the server to overwrite files, including the server itself.

Instead, use a UUID for the real filename. Store the client filename / real filename pair elsewhere.


You need to come up with a protocol so the server can distinguish between the filename and content. We could use an existing format such as JSON or YAML, but they require slurping the whole file into memory and encoding the content. You could make something up, like "the first line is the filename", but we can do a little better.

If you want to stream, we can use a stripped down HTTP protocol. Send headers as Key: Value lines. A blank line ends headers and begins sending content. For just a little extra effort we have a simple protocol that's extensible.

Here's the main loop of the server using UUID::Tiny and also autodie.

# Read Key: Value headers until we see a blank line.
my %headers;
while(my $line = <$accepter>) {
    chomp $line;
    
    last if $line eq "";

    my($key, $value) = split /\s*:\s*/, $line;
    $headers{$key} = $value;
}

# Store the file in a random filename. Do not use the client's filename
# to avoid a host of security issues.
my $filename = create_uuid_as_string(UUID_V4);
open my $fh, ">", "incoming/$filename";

# Read the content and save it to the file.
my $buf;
while( read($accepter, $buf, 1024) ) {
    print $fh $buf;
}

say "$headers{Filename} was stored in incoming/$filename";
close $my_socket;

And the client simply sends a Filename header before sending the file's content.

open my $fh, '<', $filename;

print $socket "Filename: $filename\n\n";

my $buffer ;
while( sysread($fh, $buffer , $bandwidth) ) {
    print $socket $buffer ;
}
Related