Skip to content

Commit ce35cb8

Browse files
author
Delta-Kronecker
committed
portable tor: single release artifact, prepared torrc next to exe
- config.c: use torrc next to tor.exe; default DataDirectory = <exe_dir>\data - launcher: no runtime config generation - starts tor with the prepared torrc next to tor.exe, WorkingDirectory = exe dir - fetch-bridges: writes complete relative torrc next to tor.exe (-BinDir) - build.yml: tor-win64 now merges transports + torrc + scripts into one 'tor-win64-portable' artifact; fetch-bridges runs in CI to write final torrc - package-release: downloads the single portable artifact into bin\ - remove build-portable.ps1 (CI artifact is the portable package)
1 parent 7a51aad commit ce35cb8

7 files changed

Lines changed: 166 additions & 157 deletions

File tree

.github/workflows/build.yml

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ jobs:
5050
path: build-linux.log
5151

5252
tor-win64:
53-
name: tor (Windows x86_64)
53+
name: tor-win64-portable (single release artifact)
5454
runs-on: windows-latest
55+
needs: [transports]
5556
steps:
5657
- uses: actions/checkout@v4
5758

@@ -96,18 +97,47 @@ jobs:
9697
cd "$GITHUB_WORKSPACE"
9798
./src/app/tor.exe --version
9899
99-
- name: Stage binaries + runtime DLLs
100+
- name: Stage tor + runtime DLLs
100101
shell: msys2 {0}
101102
run: |
102103
cd "$GITHUB_WORKSPACE"
103-
mkdir -p dist
104+
mkdir -p dist/scripts dist/data
104105
cp src/app/tor.exe dist/
105106
cp "$MINGW_PREFIX"/bin/libevent*.dll "$MINGW_PREFIX"/bin/libssl-3-x64.dll "$MINGW_PREFIX"/bin/libcrypto-3-x64.dll "$MINGW_PREFIX"/bin/zlib1.dll dist/ 2>/dev/null || true
106107
107-
- name: Upload artifact
108+
- name: Download transports
109+
uses: actions/download-artifact@v4
110+
with:
111+
name: transports-win64
112+
path: transports/
113+
114+
- name: Merge transports into dist
115+
shell: pwsh
116+
run: |
117+
Copy-Item "transports\*.exe" -Destination "dist\" -Force
118+
Get-ChildItem "dist" | Select-Object Name, Length
119+
120+
- name: Copy portable config, scripts, README
121+
shell: pwsh
122+
run: |
123+
Copy-Item "configs\torrc.iran" "dist\torrc" -Force
124+
Copy-Item "scripts\launcher.ps1" "dist\scripts\" -Force
125+
Copy-Item "scripts\fetch-bridges.ps1" "dist\scripts\" -Force
126+
if (Test-Path "README-iran.md") { Copy-Item "README-iran.md" "dist\" -Force }
127+
$startBat = "@echo off`r`nrem Portable Tor for Iran - always launches with the portable torrc.`r`ncd /d `"%~dp0`"`r`npowershell -NoProfile -ExecutionPolicy Bypass -File `"scripts\launcher.ps1`"`r`n"
128+
[System.IO.File]::WriteAllText("dist\start.bat", $startBat, (New-Object System.Text.ASCIIEncoding))
129+
130+
- name: Fetch fresh bridges + write final portable torrc
131+
shell: pwsh
132+
run: |
133+
& "dist\scripts\fetch-bridges.ps1" -BinDir "dist" -PerTransport 8
134+
Write-Host "--- torrc (head) ---"
135+
Get-Content "dist\torrc" -TotalCount 40
136+
137+
- name: Upload single release artifact
108138
uses: actions/upload-artifact@v4
109139
with:
110-
name: tor-win64
140+
name: tor-win64-portable
111141
path: dist/
112142

113143
- name: Upload build log

configs/torrc.iran

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Tor optimized for Iranian networks - Tor 0.4.9.x
2-
# Template: launcher.ps1 expands %%DIR%% and appends Bridge lines.
2+
# This torrc must sit NEXT TO tor.exe. All paths are relative, so the whole
3+
# folder is fully portable: copy/move it anywhere and it still works.
4+
# fetch-bridges.ps1 appends UseBridges + current Bridge lines to this file.
35

46
# --- Local proxies (Windows system proxy uses HTTP 8118) ---
57
SocksPort 127.0.0.1:9050
@@ -25,17 +27,15 @@ ConfluxEnabled 1
2527

2628
# --- IPv6 preference (often less filtered in Iran) ---
2729
ClientUseIPv6 1
28-
ClientPreferIPv6DirPort 1
2930

30-
# --- Pluggable transports (absolute paths filled in by launcher) ---
31-
UseBridges 1
32-
ClientTransportPlugin webtunnel exec %%DIR%%\webtunnel.exe
33-
ClientTransportPlugin obfs4 exec %%DIR%%\obfs4proxy.exe
34-
ClientTransportPlugin snowflake exec %%DIR%%\snowflake-client.exe
31+
# --- Pluggable transports (same folder as tor.exe) ---
32+
ClientTransportPlugin webtunnel exec webtunnel.exe
33+
ClientTransportPlugin obfs4 exec obfs4proxy.exe
34+
ClientTransportPlugin snowflake exec snowflake-client.exe
3535

36-
# --- Operational ---
37-
DataDirectory %%DATA%%
38-
Log notice file %%DATA%%\tor.log
36+
# --- Operational (data\ folder is created next to tor.exe automatically) ---
37+
DataDirectory data
38+
Log notice file data\tor.log
3939
DisableDebuggerAttachment 1
4040
AvoidDiskWrites 1
4141
SafeLogging 1

scripts/build-portable.ps1

Lines changed: 0 additions & 76 deletions
This file was deleted.

scripts/fetch-bridges.ps1

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,52 @@
11
<#
22
.SYNOPSIS
3-
Fetch fresh tested Tor bridges from the Tor-Bridges-Collector GitHub repo
4-
and write them to data\bridges.txt for launcher.ps1.
3+
Fetch fresh tested Tor bridges and write a complete portable torrc NEXT TO
4+
tor.exe (template + UseBridges + Bridge lines), plus data\bridges.txt.
55
66
.DESCRIPTION
7-
Sources (default repo Delta-Kronecker/Tor-Bridges-Collector):
7+
Bridge sources (default repo Delta-Kronecker/Tor-Bridges-Collector):
88
bridge\obfs4_tested.txt -> obfs4 bridges
99
bridge\webtunnel_tested.txt -> webtunnel bridges
1010
bridge\vanilla_tested.txt -> plain (vanilla) bridges
1111
1212
Non-routable / documentation addresses (e.g. 2001:db8::/32, RFC1918,
13-
TEST-NET) are filtered out, so placeholders never end up in bridges.txt.
13+
TEST-NET) are filtered out.
14+
15+
The resulting torrc sits next to tor.exe and uses only relative paths, so
16+
the whole folder is fully portable - no %APPDATA%, no absolute paths.
1417
1518
.EXAMPLE
1619
.\fetch-bridges.ps1
1720
.\fetch-bridges.ps1 -Transports obfs4,vanilla -PerTransport 12
21+
.\fetch-bridges.ps1 -BinDir C:\Users\me\Tor-Iran
1822
#>
1923
param(
2024
[string]$Transports = "obfs4,webtunnel,vanilla",
2125
[int]$PerTransport = 8,
2226
[string]$BaseUrl = "https://raw.githubusercontent.com/Delta-Kronecker/Tor-Bridges-Collector/refs/heads/main/bridge",
23-
[string]$OutFile = (Join-Path (Split-Path $PSScriptRoot -Parent) "data\bridges.txt")
27+
[string]$BinDir = ""
2428
)
2529

2630
$ErrorActionPreference = "Stop"
2731

32+
$Root = Split-Path $PSScriptRoot -Parent
33+
if (-not $BinDir) {
34+
$BinDir = Join-Path $Root "bin"
35+
if (-not (Test-Path (Join-Path $BinDir "tor.exe"))) { $BinDir = $Root }
36+
}
37+
if (-not (Test-Path (Join-Path $BinDir "tor.exe"))) {
38+
Write-Host "[x] tor.exe not found in $BinDir"
39+
Write-Host " Point -BinDir at the folder that contains tor.exe."
40+
exit 1
41+
}
42+
$DataDir = Join-Path $BinDir "data"
43+
$TorrcPath = Join-Path $BinDir "torrc"
44+
$Template = Join-Path $Root "configs\torrc.iran"
45+
if (-not (Test-Path $Template)) { $Template = $TorrcPath }
46+
$BridgesFile = Join-Path $DataDir "bridges.txt"
47+
48+
New-Item -ItemType Directory -Force -Path $DataDir | Out-Null
49+
2850
function Test-RoutableIpv4([string]$ip) {
2951
$o = $ip -split "\."
3052
if ($o.Count -ne 4) { return $false }
@@ -55,7 +77,6 @@ function Test-RoutableIpv6([string]$ip) {
5577
}
5678

5779
function Test-BridgeLine([string]$line) {
58-
# must contain a 40-hex fingerprint token
5980
if ($line -notmatch "\s[0-9A-Fa-f]{40}(\s|$)") { return $false }
6081
if ($line -match "obfs4\s+([0-9.]+):\d+") { return (Test-RoutableIpv4 $matches[1]) }
6182
if ($line -match "obfs4\s+\[([0-9a-fA-F:]+)\]:\d+") { return (Test-RoutableIpv6 $matches[1]) }
@@ -65,9 +86,6 @@ function Test-BridgeLine([string]$line) {
6586
return $false
6687
}
6788

68-
$dir = Split-Path $OutFile -Parent
69-
New-Item -ItemType Directory -Force -Path $dir | Out-Null
70-
7189
$all = @()
7290
$failed = @()
7391
foreach ($t in ($Transports -split ",")) {
@@ -94,7 +112,7 @@ foreach ($t in ($Transports -split ",")) {
94112
Write-Host "[ok] $t : $($valid.Count) valid, picked $($picked.Count)"
95113
$all += $picked | ForEach-Object { "Bridge $_" }
96114
} else {
97-
Write-Host "[!!] $t : 0 usable bridges (file fetched, all filtered out or unparsable)"
115+
Write-Host "[!!] $t : 0 usable bridges (all filtered out or unparsable)"
98116
$failed += $t
99117
}
100118
} catch {
@@ -104,25 +122,35 @@ foreach ($t in ($Transports -split ",")) {
104122
}
105123

106124
$all = @($all | Select-Object -Unique)
107-
if ($all.Count -eq 0) {
108-
Write-Host ""
109-
Write-Host "No bridges fetched. Check the BaseUrl / network, or add bridges manually."
110-
Write-Host "Manual fallback:"
111-
Write-Host " 1. Open https://bridges.torproject.org/options in Tor Browser"
112-
Write-Host " 2. Choose 'obfs4' or 'WebTunnel' and copy the bridge lines"
113-
Write-Host " 3. Save them to: $OutFile"
114-
Write-Host " (each line prefixed with 'Bridge ', e.g. Bridge webtunnel 1.2.3.4:443 ...)"
115-
exit 1
125+
126+
# --- regenerate the portable torrc next to tor.exe ---
127+
$base = Get-Content $Template -Raw
128+
$base = @($base -split "`n" | Where-Object { $_ -notmatch "^\s*(UseBridges\b|Bridge\b|#.*[Bb]ridge)" }) -join "`n"
129+
if ($all.Count -gt 0) {
130+
$section = "`n# --- bridges fetched on $(Get-Date -Format 'yyyy-MM-dd HH:mm') ---`n" +
131+
"UseBridges 1`n" + ($all -join "`n") + "`n"
132+
} else {
133+
$section = ""
116134
}
135+
[System.IO.File]::WriteAllText($TorrcPath, $base.TrimEnd() + $section,
136+
(New-Object System.Text.UTF8Encoding($false)))
117137

118-
$content = @(
119-
"# Bridge lines fetched on $(Get-Date -Format 'yyyy-MM-dd HH:mm') from $BaseUrl"
120-
"# Each line is used by launcher.ps1. Delete lines you do not want to use."
138+
$bridgeDoc = @(
139+
"# Bridge lines used in $TorrcPath (fetched $(Get-Date -Format 'yyyy-MM-dd HH:mm'))"
140+
"# To refresh: re-run fetch-bridges.ps1"
121141
) + $all
122-
123-
[System.IO.File]::WriteAllText($OutFile, ($content -join "`r`n") + "`r`n",
142+
[System.IO.File]::WriteAllText($BridgesFile, ($bridgeDoc -join "`r`n") + "`r`n",
124143
(New-Object System.Text.UTF8Encoding($false)))
144+
125145
Write-Host ""
126-
Write-Host "Saved $($all.Count) bridges to $OutFile"
146+
if ($all.Count -gt 0) {
147+
Write-Host "Updated $TorrcPath with $($all.Count) bridges."
148+
} else {
149+
Write-Host "No bridges fetched - torrc left WITHOUT bridges (direct connection)."
150+
Write-Host "Manual fallback:"
151+
Write-Host " 1. Open https://bridges.torproject.org/options in Tor Browser"
152+
Write-Host " 2. Choose 'obfs4' or 'WebTunnel' and copy the bridge lines"
153+
Write-Host " 3. Paste them into: $TorrcPath (add 'Bridge ' before each)"
154+
}
127155
if ($failed.Count) { Write-Host "No bridges for: $($failed -join ', ')" }
128-
Write-Host "Run launcher.ps1 to start Tor with these bridges."
156+
Write-Host "Run tor.exe (or start.bat / launcher.ps1) to connect."

0 commit comments

Comments
 (0)