ping-rust 0.1.15

Menu-driven installer and manager for the shoes proxy server
name: shoes schema compatibility

on:
  push:
    paths:
      - "src/**"
      - "tests/**"
      - "examples/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - ".github/workflows/shoes-schema.yml"
  pull_request:
    paths:
      - "src/**"
      - "tests/**"
      - "examples/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - ".github/workflows/shoes-schema.yml"
  workflow_dispatch:

permissions:
  contents: read

env:
  SHOES_COMMIT: 386b11532424b8665ee3e46340c6236fb3c47595
  SHOES_VERSION: 0.2.8

jobs:
  dry-run:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
      - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
      - name: Build fixed shoes revision
        shell: bash
        run: |
          set -euo pipefail
          shoes_source="$RUNNER_TEMP/shoes-source"
          shoes_target="$RUNNER_TEMP/shoes-target"
          git init "$shoes_source"
          git -C "$shoes_source" remote add origin https://github.com/cfal/shoes.git
          git -C "$shoes_source" fetch --depth 1 origin "$SHOES_COMMIT"
          git -C "$shoes_source" checkout --detach FETCH_HEAD
          test "$(git -C "$shoes_source" rev-parse HEAD)" = "$SHOES_COMMIT"
          CARGO_TARGET_DIR="$shoes_target" cargo build \
            --locked --release --manifest-path "$shoes_source/Cargo.toml"
          sudo install -m 0755 "$shoes_target/release/shoes" /usr/local/bin/shoes
          shoes_version="$(shoes --version)"
          printf '%s\n' "$shoes_version"
          grep -F "$SHOES_VERSION" <<<"$shoes_version"
      - name: Build ping-rust
        run: cargo build --locked
      - name: Validate and exercise chain proxy with fixed shoes revision
        env:
          PING_RUST_SHOES_E2E_BIN: /usr/local/bin/shoes
        run: |
          shoes --dry-run examples/chain-proxy.yaml
          shoes --dry-run examples/chain-e2e-upstream.yaml
          shoes --dry-run examples/chain-e2e-downstream.yaml
          shoes --dry-run examples/chain-e2e-client.yaml
          cargo test --locked --test chain_proxy_e2e -- --nocapture
      - name: Generate and validate every protocol
        shell: bash
        run: |
          set -euo pipefail
          bin="$PWD/target/debug/ping-rust"
          out="$PWD/target/shoes-validation"
          mkdir -p "$out"

          "$bin" generate reality --output "$out/reality.yaml" \
            --port 1443 --server-name www.cloudflare.com \
            --dest www.cloudflare.com:443 >/dev/null
          "$bin" generate hysteria2 --output "$out/hysteria2.yaml" \
            --port 2443 --server-name hy2.example.com --quic-endpoints 1 >/dev/null
          "$bin" generate tuic --output "$out/tuic.yaml" \
            --port 3443 --server-name tuic.example.com --zero-rtt >/dev/null
          "$bin" generate shadowsocks --output "$out/shadowsocks.yaml" \
            --port 4388 >/dev/null
          "$bin" generate anytls --output "$out/anytls.yaml" \
            --port 5443 --server-name anytls.example.com \
            --user alice:anytls-test-password \
            --padding stop=8 --padding 0=30-30 --padding 1=50-100 \
            --fallback 127.0.0.1:80 >/dev/null
          "$bin" generate vless-tls --output "$out/vless-tls.yaml" \
            --port 6443 --server-name vless-tls.example.com >/dev/null
          "$bin" generate vless-ws-tls --output "$out/vless-ws-tls.yaml" \
            --port 6444 --server-name vless-ws.example.com \
            --websocket-path /vless >/dev/null
          "$bin" generate trojan-tls --output "$out/trojan-tls.yaml" \
            --port 6445 --server-name trojan.example.com >/dev/null
          "$bin" generate trojan-reality --output "$out/trojan-reality.yaml" \
            --port 6446 --server-name www.cloudflare.com \
            --dest www.cloudflare.com:443 >/dev/null
          "$bin" generate vmess-ws-tls --output "$out/vmess-ws-tls.yaml" \
            --port 6447 --server-name vmess.example.com \
            --websocket-path /vmess >/dev/null

          for config in \
            reality hysteria2 tuic shadowsocks anytls \
            vless-tls vless-ws-tls trojan-tls trojan-reality vmess-ws-tls; do
            shoes --dry-run "$out/$config.yaml"
          done
          cat \
            "$out/reality.yaml" \
            "$out/hysteria2.yaml" \
            "$out/tuic.yaml" \
            "$out/shadowsocks.yaml" \
            "$out/anytls.yaml" \
            "$out/vless-tls.yaml" \
            "$out/vless-ws-tls.yaml" \
            "$out/trojan-tls.yaml" \
            "$out/trojan-reality.yaml" \
            "$out/vmess-ws-tls.yaml" >"$out/all-ten.yaml"
          shoes --dry-run "$out/all-ten.yaml"
      - name: Start every generated listener
        shell: bash
        run: |
          set -euo pipefail
          out="$PWD/target/shoes-validation"
          shoes "$out/all-ten.yaml" >"$out/runtime.log" 2>&1 &
          shoes_pid="$!"
          trap 'kill "$shoes_pid" 2>/dev/null || true' EXIT
          for _ in {1..40}; do
            tcp_ports="$(ss -H -lnt | awk '{print $4}')"
            udp_ports="$(ss -H -lun | awk '{print $4}')"
            if grep -Eq '(^|:)1443$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)4388$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)5443$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)6443$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)6444$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)6445$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)6446$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)6447$' <<<"$tcp_ports" \
              && grep -Eq '(^|:)2443$' <<<"$udp_ports" \
              && grep -Eq '(^|:)3443$' <<<"$udp_ports"; then
              exit 0
            fi
            sleep 0.25
          done
          cat "$out/runtime.log" >&2
          exit 1
      - name: Validate all Shadowsocks ciphers and Reality plus AnyTLS
        shell: bash
        run: |
          set -euo pipefail
          bin="$PWD/target/debug/ping-rust"
          out="$PWD/target/shoes-validation"
          ciphers=(
            aes-128-gcm
            aes-256-gcm
            chacha20-ietf-poly1305
            2022-blake3-aes-128-gcm
            2022-blake3-aes-256-gcm
            2022-blake3-chacha20-ietf-poly1305
          )
          passwords=(
            legacy-aes-128-password
            legacy-aes-256-password
            legacy-chacha-password
            AAAAAAAAAAAAAAAAAAAAAA==
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
          )
          for index in "${!ciphers[@]}"; do
            config="$out/ss-$index.yaml"
            "$bin" generate shadowsocks --output "$config" \
              --port "$((6000 + index))" \
              --cipher "${ciphers[$index]}" \
              --password "${passwords[$index]}" >/dev/null
            shoes --dry-run "$config"
          done

          "$bin" generate anytls --output "$out/anytls-reality.yaml" \
            --port 7443 --anytls-mode reality \
            --server-name www.cloudflare.com \
            --dest www.cloudflare.com:443 \
            --user advanced:anytls-reality-password >/dev/null
          shoes --dry-run "$out/anytls-reality.yaml"