Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 14 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
include:
# Windows: pure Rust — no C toolchain, no pacparser/QuickJS.
# Windows: pure Rust — no C toolchain, no QuickJS.
- name: windows x64
os: windows-latest
target: x86_64-pc-windows-msvc
Expand All @@ -26,7 +26,7 @@ jobs:
target: aarch64-pc-windows-msvc
run_tests: true

# macOS: native runners provide the C toolchain for pacparser.
# macOS: native runners provide the C toolchain for QuickJS.
- name: macos arm64
os: macos-latest
target: aarch64-apple-darwin
Expand All @@ -43,7 +43,7 @@ jobs:
run_tests: true

# Linux ARM: cross — its images ship the C cross-toolchain
# pacparser needs, and run tests under qemu.
# QuickJS needs, and run tests under qemu.
- name: linux aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
Expand All @@ -57,8 +57,6 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Rust
run: |
Expand All @@ -84,40 +82,28 @@ jobs:
run: ${{ matrix.use_cross && 'cross' || 'cargo' }} test --target ${{ matrix.target }} --all-features
shell: bash

- name: Build pactester (release)
run: ${{ matrix.use_cross && 'cross' || 'cargo' }} build --release --target ${{ matrix.target }} --example pactester
- name: Build proxytester (release)
run: ${{ matrix.use_cross && 'cross' || 'cargo' }} build --release --target ${{ matrix.target }} --example proxytester
shell: bash

- name: Stage pactester artifact
- name: Stage proxytester artifact
run: |
ext=""
lib=""
case "${{ matrix.target }}" in
*windows*) ext=".exe" ;;
*darwin*|*apple*) lib="libpacparser.dylib" ;;
*) lib="libpacparser.so" ;;
*windows*) ext=".exe" ;;
esac
stage="dist/pactester-${{ matrix.target }}"
stage="dist/proxytester-${{ matrix.target }}"
mkdir -p "$stage"
cp "target/${{ matrix.target }}/release/examples/pactester${ext}" "$stage/"
# On macOS/Linux pactester links libpacparser dynamically; ship it
# right next to the binary so the @loader_path / $ORIGIN rpath finds
# it (WinHTTP handles PAC on Windows, so there's no dylib there).
if [ -n "$lib" ]; then
found=$(find "target/${{ matrix.target }}/release/build" -name "$lib" | head -n1)
if [ -z "$found" ]; then
echo "::error::could not find $lib to bundle"
exit 1
fi
cp "$found" "$stage/"
fi
# QuickJS is statically linked into the binary, so proxytester is
# self-contained (WinHTTP handles PAC on Windows).
cp "target/${{ matrix.target }}/release/examples/proxytester${ext}" "$stage/"
shell: bash

- name: Upload pactester artifact
- name: Upload proxytester artifact
uses: actions/upload-artifact@v4
with:
name: pactester-${{ matrix.target }}
path: dist/pactester-${{ matrix.target }}/
name: proxytester-${{ matrix.target }}
path: dist/proxytester-${{ matrix.target }}/
if-no-files-found: error

os-config-tests:
Expand All @@ -135,8 +121,6 @@ jobs:
os: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Rust
run: |
Expand Down Expand Up @@ -177,8 +161,6 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust
run: |
rustup toolchain install stable --profile minimal --component rustfmt --component clippy --no-self-update
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 9 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@
name = "os-proxy-resolver"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
# First-party code is MIT (see LICENSE.txt); the bundled pacparser library
# under vendor/pacparser is LGPL-3.0-or-later (see vendor/pacparser/COPYING).
license = "MIT AND LGPL-3.0-or-later"
description = "Resolve the OS-configured proxy (static, PAC, WPAD) for a URL, with change notification. WinHTTP on Windows; SystemConfiguration/gsettings + vendored pacparser (QuickJS) on macOS/Linux."
rust-version = "1.77"
# All first-party code is MIT (see LICENSE.txt). The embedded PAC engine links
# the MIT-licensed quickjs-ng via the `rquickjs-sys` crate; the PAC helper
# functions are original, implemented from the public PAC specification.
license = "MIT"
description = "Resolve the OS-configured proxy (static, PAC, WPAD) for a URL, with change notification. WinHTTP on Windows; SystemConfiguration/gsettings + an embedded QuickJS PAC engine on macOS/Linux."
repository = "https://github.com/microsoft/os-proxy-resolver"
keywords = ["proxy", "pac", "wpad", "winhttp", "system-proxy"]
categories = ["network-programming", "os"]
exclude = [
"vendor/pacparser/tests",
"vendor/pacparser/web",
"vendor/pacparser/docs",
"vendor/pacparser/examples",
"vendor/pacparser/tools",
"vendor/pacparser/src/pymod",
]

[lib]
crate-type = ["rlib", "cdylib"]
Expand All @@ -35,6 +28,9 @@ tokio = { version = "1", optional = true, default-features = false, features = [
[target.'cfg(not(windows))'.dependencies]
# PAC / wpad.dat fetching. Sync, small, rustls-backed so no OpenSSL cross headaches.
ureq = { version = "2", default-features = false, features = ["tls", "gzip"] }
# Embedded QuickJS-NG (MIT) for the PAC engine. Vendors and compiles the
# quickjs-ng C sources via `cc`. Not used on Windows (WinHTTP handles PAC).
rquickjs-sys = "0.12.1"

[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"
Expand All @@ -49,8 +45,5 @@ windows-sys = { version = "0.60", features = [
"Win32_System_Threading",
] }

[build-dependencies]
cc = "1"

[dev-dependencies]
tokio = { version = "1", features = ["sync", "rt", "macros", "time"] }
8 changes: 4 additions & 4 deletions LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ SOFTWARE.

---

This product bundles pacparser (in vendor/pacparser), which is licensed under
the GNU Lesser General Public License, version 3. See vendor/pacparser/COPYING
for the full license text. The MIT license above applies only to the
first-party source of this project, not to the bundled pacparser library.
The embedded PAC engine (in src/pac/engine) links the QuickJS-NG JavaScript
engine on macOS and Linux via the MIT-licensed rquickjs-sys crate. QuickJS-NG
is also MIT-licensed. See ThirdPartyNotices.txt for details. The MIT license
above applies to the first-party source of this project.
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,32 @@ resolution.
| | config source | PAC + WPAD | change signal |
|---|---|---|---|
| **Windows** | `WinHttpGetIEProxyConfigForCurrentUser` | WinHTTP `WinHttpGetProxyForUrl` (PAC eval + DHCP/DNS WPAD in the OS) | registry change notification |
| **macOS** | `SCDynamicStoreCopyProxies` | vendored [pacparser] (QuickJS) + DNS WPAD | `SCDynamicStore` callback |
| **Linux** | GNOME `org.gnome.system.proxy` via `gsettings` | vendored [pacparser] (QuickJS) + DNS WPAD | `dconf watch` / `gsettings monitor` |
| **macOS** | `SCDynamicStoreCopyProxies` | built-in [QuickJS] PAC engine + DNS WPAD | `SCDynamicStore` callback |
| **Linux** | GNOME `org.gnome.system.proxy` via `gsettings` | built-in [QuickJS] PAC engine + DNS WPAD | `dconf watch` / `gsettings monitor` |

Windows builds are **pure Rust** — pacparser/QuickJS are neither compiled nor
linked there. On macOS/Linux the vendored C sources (two files:
`pacparser.c` + the QuickJS-NG amalgam) are compiled directly by `build.rs`
via the `cc` crate; no autotools/make, which keeps cross-compilation clean.
Windows builds are **pure Rust** — the QuickJS PAC engine is neither compiled
nor linked there. On macOS/Linux the PAC engine embeds QuickJS-NG via the
MIT-licensed `rquickjs-sys` crate (which compiles the QuickJS-NG C sources);
no autotools/make, which keeps cross-compilation clean. The PAC helper
functions are first-party JavaScript implemented from the public PAC
specification.

Non-goals: DHCP-based WPAD (option 252) on macOS/Linux (Windows gets it via
WinHTTP), KDE proxy settings, proxy authentication credentials.

## The PAC cage

A PAC file is untrusted JavaScript running on a live JS engine, and pacparser
has a single global, non-thread-safe context whose `dnsResolve()` /
A PAC file is untrusted JavaScript running on a live JS engine. The embedded
QuickJS context is neither `Send` nor `Sync`, and its `dnsResolve()` /
`myIpAddress()` builtins block on real network I/O. Containment:

- **One process-global worker thread** owns the pacparser context; every
init/parse/find_proxy is serialized through a command channel.
- **Hard timeout** on every `FindProxyForURL` call. A wedged evaluator (e.g.
an infinite JS loop) makes subsequent calls fail fast into the fallback
path instead of queueing; service resumes if the worker ever recovers.
- **One process-global worker thread** owns the PAC engine; every
parse/find_proxy is serialized through a command channel.
- **Hard timeout** on every `FindProxyForURL` call. A runaway JS loop is
interrupted inside the engine by its own deadline; a blocking native
builtin (e.g. slow DNS) that outlasts the caller's deadline makes
subsequent calls fail fast into the fallback path instead of queueing,
and service resumes once the worker recovers.
- **URL sanitization** before evaluation (Chromium-style): identity is always
stripped; for https URLs the path and query are dropped, so a hostile
PAC/WPAD author can't read request details.
Expand Down Expand Up @@ -112,7 +116,7 @@ is returned and retried.
## Building

```sh
git clone --recurse-submodules <repo> # pacparser is a git submodule
git clone <repo>
cargo build # needs a C compiler on macOS/Linux only
cargo test
```
Expand All @@ -122,7 +126,7 @@ Examples:
```sh
cargo run --example resolve -- https://example.com/ # live OS config
cargo run --example resolve -- --watch # watch for changes
cargo run --example pactester -- file.pac http://url/ # macOS/Linux
cargo run --example proxytester -- --pac-script file.pac http://url/ # test a PAC file
```

Builds as both `rlib` and `cdylib`. Release automation with `cargo-dist` is a
Expand All @@ -133,17 +137,17 @@ wired up yet.

GitHub Actions builds and tests: Windows x64 + arm64 (pure Rust), macOS x64 +
arm64, Linux x86_64 (native), Linux aarch64 + armv7 (via `cross`, whose images
ship the C cross-toolchain pacparser needs).
ship the C cross-toolchain QuickJS needs).

## License

The first-party code in this repository is licensed under the [MIT License](LICENSE.txt),
Copyright (c) Microsoft Corporation.

The bundled [pacparser] submodule is LGPL-3.0 and embeds QuickJS-NG (MIT); the
compiled library on macOS/Linux statically links both, so the resulting binary
is a combined work governed by `MIT AND LGPL-3.0-or-later` — check that this
suits your distribution model (in particular the LGPL relinking obligation).
Windows binaries contain neither pacparser nor QuickJS.
On macOS/Linux the built-in PAC engine embeds QuickJS-NG (MIT) via the
MIT-licensed `rquickjs-sys` crate, statically linked into the compiled library;
the PAC helper functions are first-party JavaScript implemented from the public
PAC specification. Everything is permissively licensed. Windows binaries
contain no JavaScript engine at all (WinHTTP handles PAC).

[pacparser]: https://github.com/manugarg/pacparser
[QuickJS]: https://github.com/quickjs-ng/quickjs
49 changes: 31 additions & 18 deletions ThirdPartyNotices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,23 @@ This repository incorporates material as listed below or described in the code.

---

## pacparser
## PAC engine and helper functions

Included as a git submodule under `vendor/pacparser`.

- Project: pacparser
- Homepage: https://github.com/manugarg/pacparser
- Copyright (c) 2007-2015 Manu Garg and contributors
- License: GNU Lesser General Public License, version 3 (LGPL-3.0-or-later)

The full license text is available at `vendor/pacparser/COPYING` and at
https://www.gnu.org/licenses/lgpl-3.0.html.

On macOS and Linux, pacparser is statically linked into this project's compiled
library, producing a combined work. See the LGPL-3.0 for the resulting
obligations (including the requirement to allow relinking against a modified
version of pacparser). Windows builds do not compile or link pacparser.
The PAC (Proxy Auto-Config) engine under `src/pac/engine` is first-party code
licensed under the MIT License (see LICENSE.txt). Its JavaScript helper
functions (`src/pac/engine/pac_helpers.js` and `pac_helpers_ms.js`) are
implemented solely from the public PAC specification — the Netscape "Navigator
Proxy Auto-Config File Format" document and Microsoft's published IPv6 PAC
extension documentation.

---

## QuickJS-NG
## QuickJS-NG (via rquickjs-sys)

Embedded within the pacparser sources under
`vendor/pacparser/src/quickjs`.
The PAC engine embeds the QuickJS-NG JavaScript engine on macOS and Linux,
linked through the MIT-licensed `rquickjs-sys` crate, which vendors and
compiles the QuickJS-NG C sources. Windows builds do not compile or link
QuickJS (WinHTTP handles PAC there).

- Project: QuickJS-NG (a fork of QuickJS by Fabrice Bellard and Charlie Gordon)
- Homepage: https://github.com/quickjs-ng/quickjs
Expand Down Expand Up @@ -54,3 +48,22 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading