tests: fix a race condition in ftp server disconnect

If a client disconnected and reconnected quickly, before the ftp server
had a chance to respond, the protocol message/ack (ping/pong) sequence
got out of sync, causing messages sent to the old client to be delivered
to the new.  A disconnect must now be acknowledged and intermediate
requests thrown out until it is, which ensures that such synchronization
problems can't occur. This problem could affect ftp, pop3, imap and smtp
tests.

Fixes #12002
Closes #12049
This commit is contained in:
Dan Fandrich 2023-10-06 18:18:49 -07:00
parent 500f28f414
commit f6513b9982
2 changed files with 146 additions and 26 deletions

View file

@ -670,6 +670,51 @@ sub protocolsetup {
}
}
# Perform the disconnecgt handshake with sockfilt on the secondary connection
# (the only connection we actively disconnect).
# This involves waiting for the disconnect acknowledgmeent after the DISC
# command, while throwing away anything else that might come in before
# that.
sub disc_handshake {
print DWRITE "DISC\n";
my $line;
my $nr;
while (5 == ($nr = sysread DREAD, $line, 5)) {
if($line eq "DATA\n") {
# Must read the data bytes to stay in sync
my $i;
sysread DREAD, $i, 5;
my $size = 0;
if($i =~ /^([0-9a-fA-F]{4})\n/) {
$size = hex($1);
}
read_datasockf(\$line, $size);
logmsg "> Throwing away $size bytes on closed connection\n";
}
elsif($line eq "DISC\n") {
logmsg "Fancy that; client wants to DISC, too\n";
printf DWRITE "ACKD\n";
}
elsif($line eq "ACKD\n") {
# Got the ack we were waiting for
last;
}
else {
logmsg "Ignoring: $line";
# sockfilt should not be sending us any other commands
}
}
if(!defined($nr)) {
logmsg "Error: pipe read error ($!) while waiting for ACKD";
}
elsif($nr <= 0) {
logmsg "Error: pipe EOF while waiting for ACKD";
}
}
sub close_dataconn {
my ($closed)=@_; # non-zero if already disconnected
@ -680,9 +725,7 @@ sub close_dataconn {
if(!$closed) {
if($datapid > 0) {
logmsg "Server disconnects $datasockf_mode DATA connection\n";
print DWRITE "DISC\n";
my $i;
sysread DREAD, $i, 5;
disc_handshake();
logmsg "Server disconnected $datasockf_mode DATA connection\n";
}
else {
@ -940,6 +983,7 @@ sub DATA_smtp {
elsif($line eq "DISC\n") {
# disconnect!
$disc=1;
printf SFWRITE "ACKD\n";
last;
}
else {
@ -1286,6 +1330,7 @@ sub APPEND_imap {
}
elsif($line eq "DISC\n") {
logmsg "Unexpected disconnect!\n";
printf SFWRITE "ACKD\n";
last;
}
else {
@ -2405,6 +2450,7 @@ sub STOR_ftp {
elsif($line eq "DISC\n") {
# disconnect!
$disc=1;
printf DWRITE "ACKD\n";
last;
}
else {
@ -3156,6 +3202,7 @@ while(1) {
logmsg "MAIN sockfilt said $i";
if($i =~ /^DISC/) {
# disconnect
printf SFWRITE "ACKD\n";
last;
}
next;