Skip to content

Commit 8e6f66c

Browse files
author
Delta-Kronecker
committed
tor-iran: optimized torrc (eu exits, conflux, fast failover) + launcher/bridge-fetch/package scripts
1 parent e55fdfc commit 8e6f66c

6 files changed

Lines changed: 439 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ doc/HACKING/asciidoc-helper
3030
# Editor / OS
3131
.DS_Store
3232
Thumbs.db
33+
34+
# Tor Iran runtime
35+
/bin/
36+
/data/

README-iran.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Tor Iran — بیلد بهینهشده تور برای اینترنت ایران
2+
3+
این مخزن سورس **Tor 0.4.9.11** است با یک پایپلاین CI که باینری ویندوزی و لینوکسی میسازد، بههمراه بهینهسازیها و اسکریپتهای راهاندازی مخصوص شبکه ایران.
4+
5+
## چرا این بیلد؟
6+
7+
- **WebTunnel و Snowflake** بهعنوان ترنسپورت اصلی (ترافیک داخل TLS/WebSocket به CDN های بزرگ قایم میشود؛ DPI ایران آن را بهسختی میگیرد).
8+
- **خروجی اروپا** (`ExitNodes {eu}`) برای تأخیر کمتر — به قیمت ناشناسی کمتر (اولویت: سرعت).
9+
- **مدارهای چندمسیره (Conflux)** برای دانلود سریعتر.
10+
- **`CircuitBuildTimeout 30`** برای شکست سریع و مدار سالمتر.
11+
- ترجیح **IPv6** (کمتر فیلتر میشود).
12+
13+
## ساختار
14+
15+
```
16+
configs\torrc.iran الگوی کانفیگ (توسط launcher تکمیل میشود)
17+
scripts\launcher.ps1 اجرا + تنظیم پروکسی سیستمعامل ویندوز
18+
scripts\fetch-bridges.ps1 واکشی خودکار پل از BridgeDB
19+
scripts\package-release.ps1 دانلود آخرین باینریها از GitHub Actions
20+
bin\ باینریها (tor.exe، ترنسپورتها، DLL ها) — gitignored
21+
data\ دیتای اجرایی (bridges.txt، tor.log) — gitignored
22+
.github\workflows\build.yml بیلد CI (win64/linux/transports)
23+
```
24+
25+
## نصب و راهاندازی (ویندوز)
26+
27+
1. **دریافت باینریها** — یا دانلود artifact های ران موفق از
28+
[Actions](https://github.com/Delta-Kronecker/tor/actions) یا اجرا:
29+
```
30+
$env:GH_TOKEN = "توکن"
31+
.\scripts\package-release.ps1
32+
```
33+
فایلها داخل `bin\` میآیند.
34+
35+
2. **دریافت پل (Bridge)**:
36+
```
37+
.\scripts\fetch-bridges.ps1
38+
```
39+
اگر BridgeDB از شبکهات باز نشد، از
40+
[bridges.torproject.org](https://bridges.torproject.org/) پل بگیر و در
41+
`data\bridges.txt` بگذار.
42+
43+
3. **اجرا**:
44+
```
45+
.\scripts\launcher.ps1
46+
```
47+
بعد از رسیدن به `Bootstrapped 100%`، پروکسی سیستمعامل روی
48+
`127.0.0.1:8118` تنظیم میشود. بقیه اپها (مرورگر، اپها) خودکار از آن استفاده میکنند.
49+
- SOCKS5: `127.0.0.1:9050` و DNS: `127.0.0.1:5353`
50+
- برای مدار جدید: `.\scripts\launcher.ps1 -NewCircuit`
51+
- برای توقف: کلید Enter یا `.\scripts\launcher.ps1 -Stop`
52+
53+
## بیلد دستی از سورس
54+
55+
پایپلاین در `.github/workflows/build.yml` سه خروجی میسازد:
56+
`tor-win64`، `tor-linux-x86_64`، `transports-win64`.
57+
58+
## نکته امنیتی
59+
60+
توکنهای گیتهاب را داخل فایلهای این مخزن نگذارید؛ برای push فقط در دستور استفاده کنید و بعد چرخانید.

configs/torrc.iran

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Tor optimized for Iranian networks - Tor 0.4.9.x
2+
# Template: launcher.ps1 expands %%DIR%% and appends Bridge lines.
3+
4+
# --- Local proxies (Windows system proxy uses HTTP 8118) ---
5+
SocksPort 127.0.0.1:9050
6+
HTTPTunnelPort 127.0.0.1:8118
7+
DNSPort 127.0.0.1:5353
8+
ControlPort 127.0.0.1:9051
9+
SocksPolicy accept 127.0.0.1
10+
SocksPolicy reject *
11+
12+
# --- Speed-oriented exit selection (low-latency Europe). Speed over anonymity. ---
13+
ExitNodes {eu}
14+
StrictNodes 1
15+
16+
# --- Circuit tuning: fail fast, rebuild ---
17+
LearnCircuitBuildTimeout 0
18+
CircuitBuildTimeout 30
19+
MaxCircuitDirtiness 600
20+
NumEntryGuards 2
21+
22+
# --- Multipath circuits (faster bulk transfers) ---
23+
ConfluxEnabled 1
24+
25+
# --- IPv6 preference (often less filtered in Iran) ---
26+
ClientUseIPv6 1
27+
ClientPreferIPv6DirPort 1
28+
29+
# --- Pluggable transports (absolute paths filled in by launcher) ---
30+
UseBridges 1
31+
ClientTransportPlugin webtunnel exec %%DIR%%\webtunnel.exe
32+
ClientTransportPlugin obfs4 exec %%DIR%%\obfs4proxy.exe
33+
ClientTransportPlugin snowflake exec %%DIR%%\snowflake-client.exe
34+
35+
# --- Operational ---
36+
DataDirectory %%DIR%%\..\data
37+
Log notice file %%DIR%%\..\data\tor.log
38+
Log notice stdout
39+
DisableDebuggerAttachment 1
40+
AvoidDiskWrites 1
41+
SafeLogging 1

scripts/fetch-bridges.ps1

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<#
2+
.SYNOPSIS
3+
Fetch fresh Tor bridges (webtunnel/obfs4/snowflake) from BridgeDB and
4+
write them to data\bridges.txt for launcher.ps1.
5+
6+
.EXAMPLE
7+
.\fetch-bridges.ps1
8+
.\fetch-bridges.ps1 -Transports webtunnel,obfs4
9+
#>
10+
param(
11+
[string]$Transports = "webtunnel,obfs4,snowflake",
12+
[string]$OutFile = (Join-Path (Split-Path $PSScriptRoot -Parent) "data\bridges.txt"),
13+
[int]$PerTransport = 8
14+
)
15+
16+
$ErrorActionPreference = "Stop"
17+
18+
function Get-BridgeLines([string]$transport) {
19+
$url = "https://bridges.torproject.org/bridges?transport=$transport"
20+
$ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36"
21+
$html = Invoke-WebRequest -Uri $url -Headers @{ "User-Agent" = $ua } -UseBasicParsing -TimeoutSec 30
22+
23+
$text = $html.Content
24+
# collapse common inline tags into newlines, then strip all tags
25+
foreach ($tag in @("<br", "</p>", "</div>", "</code>", "</pre>", "</span>", "</li>", "</td>", "</tr>")) {
26+
$text = $text -replace "(?i)$tag", "`n"
27+
}
28+
$text = $text -replace "(?s)<[^>]+>", "`n"
29+
$text = [System.Net.WebUtility]::HtmlDecode($text)
30+
31+
$pattern = "(?im)^\s*(?:(?:webtunnel|obfs4|snowflake)\s+[^\s]+\s+[0-9A-Fa-f]{40}[^\r\n]*)"
32+
$found = [regex]::Matches($text, $pattern) | ForEach-Object { $_.Value.Trim() } |
33+
Where-Object { $_ -match "^(webtunnel|obfs4|snowflake)\s" } |
34+
Select-Object -Unique
35+
36+
return $found
37+
}
38+
39+
$dir = Split-Path $OutFile -Parent
40+
New-Item -ItemType Directory -Force -Path $dir | Out-Null
41+
42+
$all = @()
43+
$failed = @()
44+
foreach ($t in ($Transports -split ",")) {
45+
$t = $t.Trim()
46+
try {
47+
$lines = @(Get-BridgeLines -transport $t)
48+
if ($lines.Count -gt 0) {
49+
Write-Host "[ok] $t : $($lines.Count) bridge(s) found"
50+
$all += $lines | Select-Object -First $PerTransport
51+
} else {
52+
Write-Host "[!!] $t : page fetched but no bridge lines parsed"
53+
$failed += $t
54+
}
55+
} catch {
56+
Write-Host "[!!] $t : $($_.Exception.Message)"
57+
$failed += $t
58+
}
59+
}
60+
61+
$all = $all | Select-Object -Unique
62+
if ($all.Count -eq 0) {
63+
Write-Host ""
64+
Write-Host "No bridges fetched. BridgeDB may be unreachable from your network."
65+
Write-Host "Manual fallback:"
66+
Write-Host " 1. Open https://bridges.torproject.org/options in Tor Browser"
67+
Write-Host " 2. Choose 'WebTunnel', 'obfs4' or 'Snowflake' and copy the bridge lines"
68+
Write-Host " 3. Save them to: $OutFile"
69+
Write-Host " (each line prefixed with 'Bridge ', e.g. Bridge webtunnel 1.2.3.4:443 ...)"
70+
exit 1
71+
}
72+
73+
$content = @(
74+
"# Bridge lines fetched on $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
75+
"# Each line is used by launcher.ps1. Delete lines you do not want to use."
76+
) + ($all | ForEach-Object { "Bridge $_" })
77+
78+
Set-Content -Path $OutFile -Value $content -Encoding UTF8
79+
Write-Host ""
80+
Write-Host "Saved $($all.Count) bridges to $OutFile"
81+
Write-Host "Run launcher.ps1 to start Tor with these bridges."

scripts/launcher.ps1

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<#
2+
.SYNOPSIS
3+
Tor Iran launcher: starts the optimized tor client, waits for bootstrap,
4+
and sets the Windows system proxy (HTTP 127.0.0.1:8118) on success.
5+
6+
.DESCRIPTION
7+
Layout:
8+
newway\bin\ <- tor.exe, webtunnel.exe, obfs4proxy.exe, snowflake-client.exe + DLLs
9+
newway\configs\torrc.iran
10+
newway\data\ <- bridges.txt (see fetch-bridges.ps1), tor.log, runtime torrc
11+
12+
.PARAMETER BinDir Folder containing the tor binaries (default: ..\bin relative to repo root)
13+
.PARAMETER BridgesFile File with 'Bridge ...' lines (default: data\bridges.txt)
14+
.PARAMETER BootstrapOnly Exit after reaching 100% bootstrap (no proxy change, no wait)
15+
.PARAMETER NewCircuit Send SIGNAL NEWNYM to a running tor and exit
16+
.PARAMETER Stop Stop the running tor managed by this launcher and reset proxy
17+
18+
.EXAMPLE
19+
.\launcher.ps1
20+
.\launcher.ps1 -NewCircuit
21+
#>
22+
param(
23+
[string]$BinDir,
24+
[string]$BridgesFile,
25+
[switch]$BootstrapOnly,
26+
[switch]$NewCircuit,
27+
[switch]$Stop
28+
)
29+
30+
$ErrorActionPreference = "Stop"
31+
32+
$Root = Split-Path $PSScriptRoot -Parent
33+
if (-not $BinDir) { $BinDir = Join-Path $Root "bin" }
34+
$DataDir = Join-Path $Root "data"
35+
$Template = Join-Path $Root "configs\torrc.iran"
36+
$RunTorrc = Join-Path $DataDir "torrc"
37+
$TorLog = Join-Path $DataDir "tor.log"
38+
$ConsoleLog= Join-Path $DataDir "tor-console.log"
39+
if (-not $BridgesFile) { $BridgesFile = Join-Path $DataDir "bridges.txt" }
40+
New-Item -ItemType Directory -Force -Path $DataDir | Out-Null
41+
42+
$TorExe = Join-Path $BinDir "tor.exe"
43+
44+
# ---------- control port helper ----------
45+
function Send-Control([string]$cmd) {
46+
try {
47+
$c = New-Object System.Net.Sockets.TcpClient("127.0.0.1", 9051)
48+
$s = $c.GetStream()
49+
$w = New-Object System.IO.StreamWriter($s)
50+
$r = New-Object System.IO.StreamReader($s)
51+
$w.NewLine = "`r`n"; $w.AutoFlush = $true
52+
$w.WriteLine("AUTHENTICATE"); $null = $r.ReadLine()
53+
$w.WriteLine($cmd); $null = $r.ReadLine()
54+
$w.WriteLine("QUIT")
55+
$c.Close()
56+
return $true
57+
} catch { return $false }
58+
}
59+
60+
# ---------- windows system proxy ----------
61+
function Set-SystemProxy([bool]$on) {
62+
$k = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
63+
if ($on) {
64+
Set-ItemProperty -Path $k -Name ProxyEnable -Value 1
65+
Set-ItemProperty -Path $k -Name ProxyServer -Value "127.0.0.1:8118"
66+
Set-ItemProperty -Path $k -Name ProxyOverride -Value "<local>"
67+
} else {
68+
Set-ItemProperty -Path $k -Name ProxyEnable -Value 0
69+
}
70+
Add-Type @"
71+
using System; using System.Runtime.InteropServices;
72+
public static class WinINet {
73+
[DllImport("wininet.dll", SetLastError=true)]
74+
public static extern bool InternetSetOption(IntPtr h, int o, IntPtr b, int l);
75+
public static void Refresh(){
76+
InternetSetOption(IntPtr.Zero, 39, IntPtr.Zero, 0); // SETTINGS_CHANGED
77+
InternetSetOption(IntPtr.Zero, 37, IntPtr.Zero, 0); // REFRESH
78+
}
79+
}
80+
"@
81+
[WinINet]::Refresh()
82+
}
83+
84+
# ---------- new circuit / stop ----------
85+
if ($NewCircuit) {
86+
if (Send-Control "SIGNAL NEWNYM") { Write-Host "New identity requested (NEWNYM)."; exit 0 }
87+
Write-Host "No tor control port found on 127.0.0.1:9051. Is tor running?"; exit 1
88+
}
89+
90+
$torProc = Get-Process -Name "tor" -ErrorAction SilentlyContinue |
91+
Where-Object { $_.Path -and $_.Path -eq $TorExe }
92+
if ($Stop) {
93+
if ($torProc) {
94+
$torProc | Stop-Process -Force
95+
Start-Sleep -Seconds 1
96+
Set-SystemProxy $false
97+
Write-Host "Tor stopped; system proxy reset."
98+
} else { Write-Host "No managed tor running." }
99+
exit 0
100+
}
101+
if ($torProc) {
102+
Write-Host "A tor instance from this folder is already running (PID $($torProc.Id))."
103+
Write-Host "Use -Stop to stop it, or -NewCircuit to rotate circuits."
104+
exit 0
105+
}
106+
107+
# ---------- verify binaries ----------
108+
if (-not (Test-Path $TorExe)) {
109+
Write-Host "[x] tor.exe not found in $BinDir"
110+
Write-Host " Put tor.exe + DLLs + webtunnel.exe + obfs4proxy.exe + snowflake-client.exe there."
111+
Write-Host " Download them from GitHub Actions artifacts (see README) or run package-release.ps1."
112+
exit 1
113+
}
114+
foreach ($pt in "webtunnel.exe","obfs4proxy.exe","snowflake-client.exe") {
115+
if (-not (Test-Path (Join-Path $BinDir $pt))) {
116+
Write-Warning "Missing transport: $pt (bridges of that type will not work)"
117+
}
118+
}
119+
120+
# ---------- build runtime torrc ----------
121+
if (-not (Test-Path $Template)) { Write-Host "[x] template not found: $Template"; exit 1 }
122+
$torrc = Get-Content $Template -Raw
123+
$torrc = $torrc.Replace("%%DIR%%", $BinDir)
124+
125+
$bridgeCount = 0
126+
if (Test-Path $BridgesFile) {
127+
$bridgeLines = @(Get-Content $BridgesFile | Where-Object { $_ -match "^Bridge\s+(webtunnel|obfs4|snowflake)" })
128+
$bridgeCount = $bridgeLines.Count
129+
if ($bridgeCount -gt 0) {
130+
$torrc += "`n# --- bridges (from $BridgesFile) ---`n" + ($bridgeLines -join "`n") + "`n"
131+
}
132+
}
133+
Set-Content -Path $RunTorrc -Value $torrc -Encoding UTF8
134+
Write-Host "[i] runtime torrc written to $RunTorrc (bridges: $bridgeCount)"
135+
136+
# ---------- start tor ----------
137+
if (Test-Path $TorLog) { Remove-Item $TorLog -Force }
138+
if (Test-Path $ConsoleLog) { Remove-Item $ConsoleLog -Force }
139+
140+
Write-Host "[i] starting tor.exe ..."
141+
$proc = Start-Process -FilePath $TorExe -ArgumentList "-f", $RunTorrc -PassThru -WindowStyle Hidden -RedirectStandardOutput $ConsoleLog -RedirectStandardError (Join-Path $DataDir "tor-err.log")
142+
143+
# ---------- wait for bootstrap ----------
144+
$deadline = (Get-Date).AddMinutes(6)
145+
$lastPct = -1
146+
$bootstrapped = $false
147+
try {
148+
while ((Get-Date) -lt $deadline) {
149+
if ($proc.HasExited) {
150+
Write-Host ""
151+
Write-Host "[x] tor exited with code $($proc.ExitCode)"
152+
if (Test-Path $ConsoleLog) { Get-Content $ConsoleLog -Tail 20 | ForEach-Object { Write-Host " $_" } }
153+
exit 1
154+
}
155+
if (Test-Path $TorLog) {
156+
$log = Get-Content $TorLog -ErrorAction SilentlyContinue
157+
$last = $log | Select-Object -Last 1
158+
if ($last -match "Bootstrapped 100%") { $bootstrapped = $true; break }
159+
if ($last -match "Bootstrapped (\d+)%") {
160+
$pct = [int]$matches[1]
161+
if ($pct -ne $lastPct) { Write-Host " Bootstrapped $pct% ..."; $lastPct = $pct }
162+
}
163+
}
164+
Start-Sleep -Seconds 3
165+
}
166+
} finally {
167+
if (-not $bootstrapped) {
168+
Write-Host ""
169+
Write-Host "[x] bootstrap did not reach 100% in time."
170+
Write-Host " Common fixes:"
171+
Write-Host " - your bridges may be blocked: re-run fetch-bridges.ps1 then launcher.ps1"
172+
Write-Host " - try a different transport (edit data\bridges.txt)"
173+
Write-Host " - check the tail of: $TorLog"
174+
if (Test-Path $TorLog) { Get-Content $TorLog -Tail 8 | ForEach-Object { Write-Host " $_" } }
175+
$proc | Stop-Process -Force
176+
Set-SystemProxy $false
177+
exit 1
178+
}
179+
}
180+
181+
# ---------- success ----------
182+
if ($BootstrapOnly) {
183+
Write-Host ""
184+
Write-Host "Bootstrapped 100%. Test mode: stopping tor and resetting proxy."
185+
$proc | Stop-Process -Force
186+
Set-SystemProxy $false
187+
exit 0
188+
}
189+
190+
Set-SystemProxy $true
191+
Write-Host ""
192+
Write-Host "============================================================"
193+
Write-Host " Tor is UP (100% bootstrap). System proxy set to 127.0.0.1:8118"
194+
Write-Host " SOCKS5: 127.0.0.1:9050 | DNS: 127.0.0.1:5353"
195+
Write-Host " Press Enter to stop Tor and restore the system proxy."
196+
Write-Host "============================================================"
197+
[void]$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
198+
199+
$proc | Stop-Process -Force
200+
Set-SystemProxy $false
201+
Write-Host "Tor stopped; system proxy restored."

0 commit comments

Comments
 (0)