]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tests: fix the FTP check for unexpected RST
authorGraham Campbell <hello@gjcampbell.co.uk>
Sun, 12 Jul 2026 17:47:32 +0000 (18:47 +0100)
committerJay Satiro <raysatiro@yahoo.com>
Fri, 24 Jul 2026 08:31:09 +0000 (04:31 -0400)
- In vsftpd ignore unrelated RST by matching tcpdump RSTs to the data
  connection port pair.

Prior to this change an unrelated RST on a recycled ephemeral port
could cause test failure.

Closes https://github.com/curl/curl/pull/22305

tests/http/test_30_vsftpd.py
tests/http/test_31_vsftpds.py
tests/http/test_32_ftps_vsftpd.py
tests/http/testenv/curl.py
tests/http/testenv/vsftpd.py

index d625852249a87cc5f0143780aaccef86804cdf9b..6627be2613b7b9448227b90d19e15e68ec20ac42 100644 (file)
@@ -161,7 +161,7 @@ class TestVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpd.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     # check with `tcpdump` if curl causes any TCP RST packets
     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
@@ -182,7 +182,7 @@ class TestVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpd.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     def test_30_08_active_download(self, env: Env, vsftpd: VsFTPD):
         docname = 'data-10k'
index 18f93dede4571ca4c59e1162b3e05bcfcd587390..1d8a897fbdfd61badcf22f0713dddfda9b2c0a20 100644 (file)
@@ -177,7 +177,7 @@ class TestVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpds.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     # check with `tcpdump` if curl causes any TCP RST packets
     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
@@ -197,7 +197,7 @@ class TestVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpds.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD):
         docname = 'upload-ascii'
index df1258c2d2102192c91397e64615d8762c1cacf6..1e2e9652927e178d69ad0d155d5d867aa3094a3e 100644 (file)
@@ -187,7 +187,7 @@ class TestFtpsVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpds.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     # check with `tcpdump` if curl causes any TCP RST packets
     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
@@ -207,7 +207,7 @@ class TestFtpsVsFTPD:
         # look only at ports from DATA connection.
         data_ports = vsftpds.get_data_ports(r)
         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
-        assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
+        assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets'
 
     def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD):
         docname = 'upload-ascii'
index 90b7b288567356015abdf109dab1b3550f32e937..2abbbbe49cb0c0cc4c9d0cdf8216a26ff15b662a 100644 (file)
@@ -36,7 +36,7 @@ from datetime import datetime, timedelta, timezone
 from functools import cmp_to_key
 from statistics import fmean, mean
 from threading import Thread
-from typing import Any, Dict, List, Optional, Union
+from typing import Any, Dict, List, Optional, Tuple, Union
 from urllib.parse import urlparse
 
 import psutil
@@ -188,9 +188,19 @@ class RunTcpDump:
         self._stdoutfile = os.path.join(self._run_dir, 'tcpdump.out')
         self._stderrfile = os.path.join(self._run_dir, 'tcpdump.err')
 
-    def get_rsts(self, ports: Optional[List[int]] = None) -> Optional[List[str]]:
+    def get_rsts(self, ports: Optional[List[int]] = None,
+                 port_pairs: Optional[List[Tuple[int, int]]] = None
+                 ) -> Optional[List[str]]:
         if self._proc:
             raise Exception('tcpdump still running')
+        # a pair matches only a RST between exactly these two ports, while
+        # a port in `ports` matches any RST it is involved in
+        pairs = None
+        if port_pairs is not None:
+            pairs = set()
+            for p1, p2 in port_pairs:
+                pairs.add((p1, p2))
+                pairs.add((p2, p1))
         lines = []
         with open(self._stdoutfile) as fd:
             for line in fd:
@@ -198,7 +208,10 @@ class RunTcpDump:
                 if m:
                     sport = int(m.group(1))
                     dport = int(m.group(2))
-                    if ports is None or sport in ports or dport in ports:
+                    if pairs is not None:
+                        if (sport, dport) in pairs:
+                            lines.append(line)
+                    elif ports is None or sport in ports or dport in ports:
                         lines.append(line)
         return lines
 
index daaf904f1e4e56eef605440d1584f64cf46d586a..e2c33edee33babf5a6e0d29686c5a61714b8e633 100644 (file)
@@ -31,7 +31,7 @@ import socket
 import subprocess
 import time
 from datetime import datetime, timedelta
-from typing import Dict, List
+from typing import Dict, List, Tuple
 
 from .curl import CurlClient, ExecResult
 from .env import Env
@@ -226,6 +226,7 @@ class VsFTPD:
         with open(self._conf_file, 'w') as fd:
             fd.write("\n".join(conf))
 
-    def get_data_ports(self, r: ExecResult) -> List[int]:
-        return [int(m.group(1)) for line in r.trace_lines if
-                (m := re.match(r'.*Established 2nd connection to .* \(\S+ port (\d+)\)', line))]
+    def get_data_ports(self, r: ExecResult) -> List[Tuple[int, int]]:
+        return [(int(m.group(1)), int(m.group(2))) for line in r.trace_lines if
+                (m := re.match(r'.*Established 2nd connection to .* '
+                               r'\(\S+ port (\d+)\) from \S+ port (\d+)', line))]