mirror of
https://github.com/necronicle/z2k.git
synced 2026-04-28 11:30:30 +00:00
IPv6 (mtproxy-client): - listener.go: dual-stack SO_ORIGINAL_DST — tries IPv4 first, falls back to IPv6 via SOL_IPV6/IP6T_SO_ORIGINAL_DST (sockaddr_in6 parsing) - dcmap.go: add Telegram IPv6 CIDR ranges (2001:b28:f23d::/48 → DC2, 2001:b28:f23f::/48 → DC5, 2001:67c:4e8::/48 → DC2) - main.go: resolveIP() prefers IPv4, accepts IPv6; connectWS uses "tcp" dual-stack dial as fallback - transparent.go: resolveIPCached replaces resolveIPv4Cached, supports both address families - Tests: TestLookupDC_IPv6 covers all new ranges Web monitoring panel: - z2k-webpanel.sh: CGI script for busybox httpd with dark theme, service status, strategies, autocircular state, logs, system info, action buttons (restart/stop/start/clearstate), auto-refresh 30s - z2k-webpanel-install.sh: installer for busybox httpd setup - Integrated into install.sh and z2k.sh bootstrap downloads ECH (Encrypted Client Hello) support: - z2k_detect_ech(): detects TLS extension type 0xfe0d in ClientHello - z2k_ech_passthrough(): desync action that skips processing when ECH is present (DPI cannot see SNI, desync unnecessary) - z2k_strategy_profile(): latency/success tracking per strategy Lua hardening: - TOCTOU in file permission checks documented — actual safety via lock+rename pattern (already correct, added explanation comment) Integration test framework (86 tests total): - test_config_official.sh: NFQWS2_OPT generation, Austerus mode, circular nld2 injection, failure detector injection - test_strategies.sh: strategy parsing, empty/malformed input handling, category file creation, get_strategy retrieval - test_validator.sh: config validation, port ranges, hostlist checks, missing config/variables detection - run_all.sh: test runner with summary Other: - UPSTREAM_PROPOSALS.md: 6 improvement proposals for bol-van/zapret2 - Fix grep -c whitespace in generate_strategies_conf Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
101 lines
2.2 KiB
Go
101 lines
2.2 KiB
Go
package main
|
|
|
|
import "net"
|
|
|
|
// dcEntry maps a CIDR to a Telegram DC number.
|
|
type dcEntry struct {
|
|
cidr *net.IPNet
|
|
dc int16
|
|
}
|
|
|
|
var dcTable []dcEntry
|
|
|
|
func init() {
|
|
// Telegram DC IP ranges -> DC number.
|
|
// Negative DC = media DC (abs value is the DC).
|
|
entries := []struct {
|
|
cidr string
|
|
dc int16
|
|
}{
|
|
// DC1
|
|
{"149.154.175.0/24", 1},
|
|
// DC2
|
|
{"149.154.167.0/24", 2},
|
|
{"95.161.76.0/24", 2},
|
|
// DC3
|
|
{"149.154.175.100/32", 3},
|
|
// DC4
|
|
{"149.154.167.91/32", 4},
|
|
// DC5
|
|
{"149.154.171.0/24", 5},
|
|
{"91.108.56.0/22", 5},
|
|
// General Telegram ranges (default to DC2)
|
|
{"91.108.4.0/22", 2},
|
|
{"91.108.8.0/22", 2},
|
|
{"91.108.12.0/22", 2},
|
|
{"91.108.16.0/22", 2},
|
|
{"91.108.20.0/22", 2},
|
|
{"149.154.160.0/20", 2},
|
|
{"185.76.151.0/24", 2},
|
|
{"91.105.192.0/23", 2},
|
|
{"95.161.64.0/20", 2},
|
|
|
|
// IPv6 Telegram ranges
|
|
{"2001:b28:f23d::/48", 2}, // DC2 main IPv6
|
|
{"2001:b28:f23f::/48", 5}, // DC5 IPv6
|
|
{"2001:67c:4e8::/48", 2}, // General Telegram IPv6
|
|
}
|
|
|
|
for _, e := range entries {
|
|
_, cidr, err := net.ParseCIDR(e.cidr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
dcTable = append(dcTable, dcEntry{cidr: cidr, dc: e.dc})
|
|
}
|
|
}
|
|
|
|
// LookupDC returns the Telegram DC number for the given IP.
|
|
// Returns 2 (default DC) if no match found. Supports both IPv4 and IPv6.
|
|
func LookupDC(ip net.IP) int16 {
|
|
isV6 := ip.To4() == nil
|
|
|
|
if !isV6 {
|
|
// IPv4: check most specific first (single IPs for DC3/DC4).
|
|
for _, e := range dcTable {
|
|
ones, _ := e.cidr.Mask.Size()
|
|
if ones == 32 && e.cidr.IP.Equal(ip) {
|
|
return e.dc
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check all CIDR ranges (both v4 and v6).
|
|
// For IPv4, skip /32 (already checked above). For IPv6, check all.
|
|
bestOnes := -1
|
|
bestDC := int16(0)
|
|
for _, e := range dcTable {
|
|
ones, bits := e.cidr.Mask.Size()
|
|
if !isV6 && ones == 32 {
|
|
continue // already handled above for v4
|
|
}
|
|
if e.cidr.Contains(ip) {
|
|
// Pick the most specific (longest prefix) match.
|
|
// Normalize: compare relative specificity (ones out of bits).
|
|
// For same address family, just compare ones directly.
|
|
specificity := ones
|
|
if bits == 128 {
|
|
// IPv6 range
|
|
specificity = ones
|
|
}
|
|
if specificity > bestOnes {
|
|
bestOnes = specificity
|
|
bestDC = e.dc
|
|
}
|
|
}
|
|
}
|
|
if bestOnes >= 0 {
|
|
return bestDC
|
|
}
|
|
return 2
|
|
}
|