Automated HITSZ (Harbin Institute of Technology, Shenzhen) campus network authentication.
Two independent implementations sharing the same operational logic:
| Platform | Approach | Status |
|---|---|---|
| Python daemon (macOS) | Pure HTTP — Srun portal API | Active |
| Android App (Kotlin) | WebView + JavaScript injection | Active |
Both detect captive portals by probing baidu.com and authenticate against the campus Srun portal on a 60-second loop.
# Install deps
uv sync
# Create config
cat > .env << EOF
HITSZ_USERNAME=your_student_id
HITSZ_PASSWORD=your_password
EOF
# Single check
uv run hitsz_net/hitsz_net.py --once
# Install as background service (auto-starts on boot)
uv run service/install.py install --config .env
# Manage
uv run service/install.py status
uv run service/install.py uninstallLogs: ~/Library/Logs/hitsz-autonet/service.log
check_internet() ──→ baidu.com reachable? ──→ OK, sleep 60s
│ no (captive portal redirect)
└──→ GET /cgi-bin/get_challenge
Compute HMAC-MD5 + XXTEA-encrypted credentials
GET /cgi-bin/srun_portal
Verify connectivity
No browser, no ChromeDriver — pure HTTP requests against the Srun portal API.
On macOS, the daemon remembers each account's observed interface/IP sessions in ~/Library/Application Support/hitsz-autonet/session-state.json (mode 0600). This survives DHCP changes and restarts.
Each monitor cycle:
- finds the current default interface and its IPv4 address;
- queries every remembered non-current IP directly with
rad_user_info?ip=<historical-ip>; - requires the historical IP to still be online under the configured account;
- reports a MAC change through a notification, but does not block logout because macOS private/random MACs can change;
- rechecks the default route, sends an IP-targeted
rad_user_dm, and requires a follow-up query to report the old IP offline before deleting it from state; and - records the current default-interface session after a successful login or online check.
The daemon never chooses an unrelated account session. When Srun rejects login because the online-device limit was reached (for example E2620), the notification includes the portal code/message, current interface/IP, device total, and any returned device summary.
cd android-app
export JAVA_HOME=/opt/homebrew/opt/openjdk@17
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkSee android-app/README.md for details.
hitsz-autonet/
├── hitsz_net/ # Python daemon
│ ├── hitsz_net.py # Main daemon (connectivity check, login, notifications, daemon loop)
│ └── srun_crypto.py # Srun portal crypto (XXTEA, custom base64, HMAC-MD5, SHA1)
├── service/ # macOS LaunchAgent installer
│ └── install.py
├── android-app/ # Android app (Kotlin, Gradle)
│ └── app/src/main/java/com/hitsz/autonet/
├── requirements.txt # Python deps (pip-compatible)
└── AGENTS.md # AI assistant guidelines
- Python >= 3.13 (managed via
uv) - Android: JDK 17, Gradle 8.7, AGP 8.5.0, minSdk 24
MIT — adapted from siliconx/hitsz_net.