diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 98db65f2..b15e29c3 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,3 +16,4 @@ jobs: - name: check that implementations.json is valid if: success() || failure() # run this step even if the previous one failed run: python implementations.py + - uses: ludeeus/action-shellcheck@master diff --git a/certs.sh b/certs.sh index 174a120a..f7c19f53 100755 --- a/certs.sh +++ b/certs.sh @@ -19,7 +19,7 @@ openssl req -x509 -sha256 -nodes -days 10 -key "$CERTDIR"/ca_0.key \ -subj "/O=interop runner Root Certificate Authority/" \ -config cert_config.txt \ -extensions v3_ca \ - 2> /dev/null + 2>/dev/null # Inflate certificate for the amplification test fakedns="" @@ -39,21 +39,21 @@ for i in $(seq 1 "$CHAINLEN"); do openssl ecparam -name prime256v1 -genkey -out "$CERTDIR"/ca_"$i".key openssl req -out "$CERTDIR"/cert.csr -new -key "$CERTDIR"/ca_"$i".key -nodes \ -subj "/O=$SUBJ/" \ - 2> /dev/null + 2>/dev/null # Sign the certificate - j=$((i-1)) + j=$((i - 1)) if [[ $i < "$CHAINLEN" ]]; then openssl x509 -req -sha256 -days 10 -in "$CERTDIR"/cert.csr -out "$CERTDIR"/cert_"$i".pem \ -CA "$CERTDIR"/cert_"$j".pem -CAkey "$CERTDIR"/ca_"$j".key -CAcreateserial \ -extfile cert_config.txt \ -extensions v3_ca \ - 2> /dev/null + 2>/dev/null else openssl x509 -req -sha256 -days 10 -in "$CERTDIR"/cert.csr -out "$CERTDIR"/cert_"$i".pem \ -CA "$CERTDIR"/cert_"$j".pem -CAkey "$CERTDIR"/ca_"$j".key -CAcreateserial \ - -extfile <(printf "subjectAltName=DNS:server,DNS:server4,DNS:server6,DNS:server46$fakedns") \ - 2> /dev/null + -extfile <(printf "subjectAltName=DNS:server,DNS:server4,DNS:server6,DNS:server46%s" "$fakedns") \ + 2>/dev/null fi done @@ -62,7 +62,7 @@ cp "$CERTDIR"/ca_"$CHAINLEN".key "$CERTDIR"/priv.key # combine certificates for i in $(seq "$CHAINLEN" -1 1); do - cat "$CERTDIR"/cert_"$i".pem >> "$CERTDIR"/cert.pem + cat "$CERTDIR"/cert_"$i".pem >>"$CERTDIR"/cert.pem rm "$CERTDIR"/cert_"$i".pem "$CERTDIR"/ca_"$i".key done rm -f "$CERTDIR"/*.srl "$CERTDIR"/ca_0.key "$CERTDIR"/cert.csr diff --git a/interop.py b/interop.py index 41fd699f..d6b27cea 100644 --- a/interop.py +++ b/interop.py @@ -180,27 +180,29 @@ def _check_impl_is_compliant(self, name: str) -> bool: def _postprocess_results(self): clients = list(set(client for client, _ in self._client_server_pairs)) servers = list(set(server for _, server in self._client_server_pairs)) - # If a client failed a test against all servers, make the test unsupported for the client questionable = [TestResult.FAILED, TestResult.UNSUPPORTED] - for c in set(clients) - set(self._no_auto_unsupported): - for t in self._tests: - if all(self.test_results[s][c][t] in questionable for s in servers): - print( - f'Client {c} failed test "{t.name()}" against all servers, ' - + 'marking the entire test as "unsupported"' - ) - for s in servers: - self.test_results[s][c][t] = TestResult.UNSUPPORTED + # If a client failed a test against all servers, make the test unsupported for the client + if len(servers) > 1: + for c in set(clients) - set(self._no_auto_unsupported): + for t in self._tests: + if all(self.test_results[s][c][t] in questionable for s in servers): + print( + f"Client {c} failed or did not support test {t.name()} " + + 'against all servers, marking the entire test as "unsupported"' + ) + for s in servers: + self.test_results[s][c][t] = TestResult.UNSUPPORTED # If a server failed a test against all clients, make the test unsupported for the server - for s in set(servers) - set(self._no_auto_unsupported): - for t in self._tests: - if all(self.test_results[s][c][t] in questionable for c in clients): - print( - f'Server {s} failed test "{t.name()}" against all clients, ' - + 'marking the entire test as "unsupported"' - ) - for c in clients: - self.test_results[s][c][t] = TestResult.UNSUPPORTED + if len(clients) > 1: + for s in set(servers) - set(self._no_auto_unsupported): + for t in self._tests: + if all(self.test_results[s][c][t] in questionable for c in clients): + print( + f"Server {s} failed or did not support test {t.name()} " + + 'against all clients, marking the entire test as "unsupported"' + ) + for c in clients: + self.test_results[s][c][t] = TestResult.UNSUPPORTED def _print_results(self): """print the interop table""" diff --git a/testcases.py b/testcases.py index ced187ea..b38c24d9 100644 --- a/testcases.py +++ b/testcases.py @@ -302,6 +302,8 @@ def get_paths(self): @abc.abstractmethod def check(self) -> TestResult: + self._client_trace() + self._server_trace() pass @@ -338,6 +340,7 @@ def get_paths(self): return [""] def check(self) -> TestResult: + super().check() tr = self._client_trace() initials = tr.get_initial(Direction.FROM_CLIENT) dcid = "" @@ -373,6 +376,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() if not self._check_version_and_files(): return TestResult.FAILED if self._retry_sent(): @@ -408,6 +412,7 @@ def scenario() -> str: return "simple-p2p --delay=750ms --bandwidth=10Mbps --queue=25" def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -453,6 +458,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -484,6 +490,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -528,6 +535,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -609,6 +617,7 @@ def _check_trace(self) -> bool: return False def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -641,6 +650,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -702,6 +712,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 2: logging.info("Expected exactly 2 handshakes. Got: %d", num_handshakes) @@ -744,6 +755,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -787,6 +799,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -898,6 +911,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -931,6 +945,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -1026,6 +1041,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != self._num_runs: logging.info( @@ -1065,6 +1081,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) @@ -1144,6 +1161,7 @@ def _check_ack_ecn(self, tr) -> bool: return False def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -1242,6 +1260,7 @@ def _path(p: List) -> Tuple[str, int, str, int]: ) def check(self) -> TestResult: + super().check() if not self._keylog_file(): logging.info("Can't check test result. SSLKEYLOG required.") return TestResult.UNSUPPORTED @@ -1330,7 +1349,34 @@ def scenario() -> str: ) def check(self) -> TestResult: - return super(TestCaseAddressRebinding, self).check() + super().check() + if not self._keylog_file(): + logging.info("Can't check test result. SSLKEYLOG required.") + return TestResult.UNSUPPORTED + + tr_server = self._server_trace()._get_packets( + self._server_trace()._get_direction_filter(Direction.FROM_SERVER) + " quic" + ) + + ips = set() + for p in tr_server: + ip_vers = "ip" + if "IPV6" in str(p.layers): + ip_vers = "ipv6" + ips.add(getattr(p[ip_vers], "dst")) + + logging.info("Server saw these client addresses: %s", ips) + if len(ips) <= 1: + logging.info( + "Server saw only a single client IP address in use; test broken?" + ) + return TestResult.FAILED + + result = super(TestCaseAddressRebinding, self).check() + if result != TestResult.SUCCEEDED: + return result + + return TestResult.SUCCEEDED class TestCaseIPv6(TestCaseTransfer): @@ -1362,6 +1408,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() result = super(TestCaseIPv6, self).check() if result != TestResult.SUCCEEDED: return result @@ -1413,6 +1460,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() # The parent check() method ensures that the client changed addresses # and that PATH_CHALLENGE/RESPONSE frames were sent and received result = super(TestCaseConnectionMigration, self).check() @@ -1471,6 +1519,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() # Client should initially send QUIC v1 packet. It may send # QUIC v2 packet. versions = self._get_packet_versions( @@ -1574,6 +1623,7 @@ def get_paths(self): return self._files def check(self) -> TestResult: + super().check() num_handshakes = self._count_handshakes() if num_handshakes != 1: logging.info("Expected exactly 1 handshake. Got: %d", num_handshakes) diff --git a/web/cleanup.sh b/web/cleanup.sh index 758205ec..b7c4e586 100755 --- a/web/cleanup.sh +++ b/web/cleanup.sh @@ -3,21 +3,21 @@ # This script is used to delete old logs from the log directory. # It removes log directories it deletes from logs.json. -die () { +die() { echo "$0 " exit 1 } -if [ -z $1 ] || [ -z $2 ] ; then +if [ -z "$1" ] || [ -z "$2" ]; then die fi LOGDIR=$1 AGE=$2 -find $LOGDIR -maxdepth 1 -type d -mtime +$AGE | while read line; do - DIR=`basename $line` +find "$LOGDIR" -maxdepth 1 -type d -mtime "+$AGE" | while read -r line; do + DIR=$(basename "$line") echo "Deleting $DIR" - jq ". - [ \"$DIR\" ]" $LOGDIR/logs.json | sponge $LOGDIR/logs.json - rm -rf $line + jq ". - [ \"$DIR\" ]" "$LOGDIR/logs.json" | sponge "$LOGDIR/logs.json" + rm -rf "$line" done