ipv6-bridge 1.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -31
- package/docs/API.md +365 -0
- package/docs/ARCHITECTURE.md +218 -0
- package/docs/CHANGELOG.md +159 -0
- package/docs/CONTRIBUTING.md +88 -0
- package/docs/GUIDE.md +675 -0
- package/docs/ROADMAP.md +69 -0
- package/examples/basic-usage.js +31 -23
- package/examples/embedded-usage.js +100 -0
- package/examples/production-usage.js +106 -0
- package/package.json +6 -6
- package/src/agent.js +271 -0
- package/src/cache.js +108 -0
- package/src/cli.js +265 -48
- package/src/config.js +159 -24
- package/src/connect.js +142 -0
- package/src/detect.js +82 -43
- package/src/discovery.js +122 -0
- package/src/dns64.js +226 -44
- package/src/doctor.js +222 -0
- package/src/index.js +157 -62
- package/src/ipv6.js +232 -0
- package/src/logger.js +47 -0
- package/src/netmatch.js +122 -0
- package/src/proxy.js +419 -101
- package/src/socks5.js +283 -0
- package/src/stats.js +140 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.1.1] - 2026-09-13
|
|
9
|
+
|
|
10
|
+
A final-audit pass over 2.1.0's embeddable API, before its first npm publish,
|
|
11
|
+
found one critical bug and one contract violation. Neither shipped to npm.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **A malformed TLS option crashed the host process.** `BridgeHttpsAgent` and
|
|
16
|
+
`createConnector()` called `tls.connect()` in a way that let a synchronous
|
|
17
|
+
throw (from an invalid `secureProtocol`, cipher list, or similar) escape as
|
|
18
|
+
an uncaught exception rather than a request error — on the very first
|
|
19
|
+
request, before any application-level error handler had a chance to run.
|
|
20
|
+
Reproduced directly: a bad `secureProtocol` value took down the entire
|
|
21
|
+
process with no `error` event and no rejected promise to catch. Both now
|
|
22
|
+
route connection setup through a single guarded path that always reports
|
|
23
|
+
failure through the callback.
|
|
24
|
+
- `createLookup()` silently ignored the documented `dns.lookup(hostname,
|
|
25
|
+
family, callback)` integer-shorthand form, since `(4).family` is
|
|
26
|
+
`undefined`. It now handles that form (and a `null` options argument), and
|
|
27
|
+
errors with `EAI_ADDRFAMILY` rather than silently returning a different
|
|
28
|
+
family when the requested one isn't available.
|
|
29
|
+
|
|
30
|
+
## [2.1.0] - 2026-09-13
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **Embedded API.** The bridge can now be used from inside an application, with
|
|
35
|
+
no proxy and no system configuration. Previously the only way to use it was to
|
|
36
|
+
run a proxy and point clients at it, which is impractical for a deployed
|
|
37
|
+
service.
|
|
38
|
+
- `createAgent()` and `createHttpsAgent()` — drop-in agents for `http`/`https`
|
|
39
|
+
and any client that accepts one (axios, got, node-fetch). Connections try
|
|
40
|
+
native IPv6, then NAT64 synthesis, then direct IPv4, with keep-alive pooling.
|
|
41
|
+
TLS certificates are validated against the requested hostname rather than
|
|
42
|
+
the synthesized address the connection travelled over.
|
|
43
|
+
- `createAgents()` — both protocols at once.
|
|
44
|
+
- `createLookup()` — a `dns.lookup`-compatible function for anything taking a
|
|
45
|
+
`lookup` option, including `net.connect` and many database drivers.
|
|
46
|
+
- `createConnector()` — an undici connector, so Node's global `fetch` can use
|
|
47
|
+
the bridge. undici remains outside this package's dependencies.
|
|
48
|
+
- `resolve()` — report which route would be taken, without connecting.
|
|
49
|
+
- `getStats()` — the data behind `/status`, available to embedded users so a
|
|
50
|
+
service can alert when translation stops happening.
|
|
51
|
+
- A test fixture certificate covering `localhost` and `127.0.0.1`, so TLS
|
|
52
|
+
behaviour is verified locally without network access.
|
|
53
|
+
|
|
54
|
+
## [2.0.0] - 2026-09-13
|
|
55
|
+
|
|
56
|
+
A correctness, security and production-readiness release. An audit found that
|
|
57
|
+
the plain-HTTP proxy path never worked with real proxy clients, that the
|
|
58
|
+
listener was an open relay by default, and that failed translation was
|
|
59
|
+
indistinguishable from success. All of that is fixed here.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **Plain HTTP proxying never worked.** Request targets were built by
|
|
64
|
+
concatenating the `Host` header with `req.url`. Real proxy clients send
|
|
65
|
+
absolute-form targets (RFC 7230 section 5.3.2), so every request produced a
|
|
66
|
+
malformed URL and returned `500`. Absolute-form is now parsed directly, with
|
|
67
|
+
origin-form accepted as a fallback.
|
|
68
|
+
- **Proxy credentials leaked to origin servers.** `Proxy-Authorization` and
|
|
69
|
+
other hop-by-hop headers were forwarded verbatim. They are now stripped in
|
|
70
|
+
both directions per RFC 7230 section 6.1, including headers named by the
|
|
71
|
+
`Connection` header.
|
|
72
|
+
- **CONNECT to IPv6 literals was broken.** `[::1]:443` was split on every colon,
|
|
73
|
+
yielding the hostname `[`, and the client hung with no response. Authority
|
|
74
|
+
parsing now handles bracketed literals, bare literals and `host:port`.
|
|
75
|
+
- **Detection reported a false positive on dual-stack networks.** `needsBridge()`
|
|
76
|
+
never checked whether IPv4 already worked, so the bridge activated on healthy
|
|
77
|
+
networks where it could only cause harm. IPv4 reachability is now the first check.
|
|
78
|
+
- **DNS resolution failed on DoH-only and split-DNS hosts.** Resolution used
|
|
79
|
+
`dns.resolve*`, which bypasses the system resolver, hosts file and
|
|
80
|
+
DNS-over-HTTPS configuration. It now uses `dns.lookup`.
|
|
81
|
+
- **Failed translation was silent.** A DNS64 failure fell through to a direct
|
|
82
|
+
IPv4 connection with no log, making a non-functioning bridge look like a
|
|
83
|
+
working one. Fallbacks now log a warning and are counted separately.
|
|
84
|
+
- **`stop()` never resolved with an open CONNECT tunnel**, hanging any test
|
|
85
|
+
suite that used the programmatic API. Live sockets are now torn down.
|
|
86
|
+
- **Concurrent `start()` calls leaked an uncloseable server.** The guard ran
|
|
87
|
+
before an `await`, so both callers passed it. The second call now rejects.
|
|
88
|
+
- **Every DNS resolution leaked a 5-second timer**, keeping the event loop alive
|
|
89
|
+
long after the work finished. The timeout is now cleared.
|
|
90
|
+
- **CONNECT failures closed the socket silently**; they now return `502` or `504`.
|
|
91
|
+
- Path traversal in the demo application allowed reading files outside its
|
|
92
|
+
public directory, and its "bridge running" indicator reported the status of an
|
|
93
|
+
unrelated website. Both are fixed, and the demo now binds to loopback.
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
|
|
97
|
+
- **Full RFC 6052 prefix support** — `/32`, `/40`, `/48`, `/56`, `/64` and `/96`,
|
|
98
|
+
validated against the official section 2.4 test vectors, including the
|
|
99
|
+
reserved `u` octet at bits 64–71.
|
|
100
|
+
- **NAT64 prefix discovery (RFC 7050)** — the bridge resolves `ipv4only.arpa` at
|
|
101
|
+
startup to learn the prefix the network actually uses, instead of assuming the
|
|
102
|
+
well-known one.
|
|
103
|
+
- **SOCKS5 listener (RFC 1928/1929)** — carries any TCP protocol, so ssh, git and
|
|
104
|
+
database clients can use the same translation. Enable with `IPV6_BRIDGE_SOCKS_PORT`.
|
|
105
|
+
- **`ipv6-bridge doctor`** — diagnoses resolvers, connectivity, NAT64 availability,
|
|
106
|
+
prefix mismatches and listener exposure, and explains what each result means.
|
|
107
|
+
- **Operational endpoints** — `/healthz`, `/status`, `/metrics` (Prometheus) and
|
|
108
|
+
`/proxy.pac`. `/status` reports a `translationRate` so operators can confirm
|
|
109
|
+
the bridge is actually translating.
|
|
110
|
+
- **Connection failover** — candidate addresses are tried in preference order
|
|
111
|
+
(native IPv6, then NAT64, then direct IPv4) instead of giving up on the first.
|
|
112
|
+
- **DNS caching** with a bounded LRU and TTL expiry.
|
|
113
|
+
- **Keep-alive connection pooling** for upstream HTTP requests.
|
|
114
|
+
- **Authentication and access control** — `IPV6_BRIDGE_AUTH` for Basic
|
|
115
|
+
credentials and `IPV6_BRIDGE_ALLOW` for a client CIDR allowlist, enforced on
|
|
116
|
+
the HTTP, CONNECT and SOCKS5 paths.
|
|
117
|
+
- **Bypass rules** — `IPV6_BRIDGE_BYPASS` routes matching hosts directly.
|
|
118
|
+
- Continuous integration across Node 18/20/22 on Linux, Windows and macOS.
|
|
119
|
+
|
|
120
|
+
### Changed
|
|
121
|
+
|
|
122
|
+
- **The proxy now binds to `127.0.0.1` by default** instead of all interfaces.
|
|
123
|
+
This is a breaking change: previously any host on the same network could relay
|
|
124
|
+
traffic through it without authentication. Set `IPV6_BRIDGE_HOST` to expose it
|
|
125
|
+
deliberately, and pair that with `IPV6_BRIDGE_AUTH` or `IPV6_BRIDGE_ALLOW`.
|
|
126
|
+
- Non-global IPv4 addresses are no longer synthesized with the well-known prefix
|
|
127
|
+
(RFC 6052 section 3.1); they are reached directly over IPv4 instead. This makes
|
|
128
|
+
targets such as `127.0.0.1` and `192.168.x.x` work through the proxy.
|
|
129
|
+
- Synthesized addresses are emitted in RFC 5952 canonical form, so
|
|
130
|
+
`64:ff9b::0808:0808` is now written `64:ff9b::808:808`. The address is unchanged.
|
|
131
|
+
- Configuration is validated at startup; an invalid prefix, port or timeout fails
|
|
132
|
+
immediately with an explanatory message.
|
|
133
|
+
- `start()` accepts an options object (`host`, `force`, `discoverPrefix`, `socksPort`).
|
|
134
|
+
- The logger gained a `silent` level, and `index.js` no longer writes directly to
|
|
135
|
+
the console, so library consumers can suppress output.
|
|
136
|
+
- Detection endpoints are configurable via `IPV4_TEST_URL`, `IPV6_TEST_URL` and
|
|
137
|
+
`NAT64_TEST_HOST`, and accept any 2xx/3xx response as reachable.
|
|
138
|
+
- `npm test` no longer relies on shell glob expansion, so it works on Windows.
|
|
139
|
+
- Tests run entirely against local servers and need no network access.
|
|
140
|
+
|
|
141
|
+
### Removed
|
|
142
|
+
|
|
143
|
+
- `.npmignore`, which was redundant with the `files` field in `package.json` and
|
|
144
|
+
listed paths that no longer existed.
|
|
145
|
+
|
|
146
|
+
## [1.0.0] - 2026-01-31
|
|
147
|
+
|
|
148
|
+
### Added
|
|
149
|
+
|
|
150
|
+
- DNS64 resolver for IPv4-to-IPv6 address synthesis (RFC 6052)
|
|
151
|
+
- HTTP/HTTPS proxy with NAT64 routing (RFC 6146)
|
|
152
|
+
- Auto-detection of IPv6-only networks (`needsBridge()`)
|
|
153
|
+
- IP version detection (`detectIPVersion()`)
|
|
154
|
+
- Command-line interface (`npx ipv6-bridge start`)
|
|
155
|
+
- Programmatic API (`start()` / `stop()`)
|
|
156
|
+
- Environment variable configuration (`IPV6_BRIDGE_PORT`, `FORCE_BRIDGE`, `NAT64_PREFIX`)
|
|
157
|
+
- Interactive demo application with diagnostics
|
|
158
|
+
- Dual-stack test server for manual testing
|
|
159
|
+
- Comprehensive test suite
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to IPv6 Bridge!
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Fork and clone the repository.
|
|
8
|
+
2. Install dependencies: `npm install`
|
|
9
|
+
3. Run the tests: `npm test`
|
|
10
|
+
|
|
11
|
+
## Development Workflow
|
|
12
|
+
|
|
13
|
+
1. Create a feature branch from `main`.
|
|
14
|
+
2. Make your changes with clear, focused commits.
|
|
15
|
+
3. Add or update tests for any new functionality.
|
|
16
|
+
4. Ensure all tests pass: `npm test`
|
|
17
|
+
5. Submit a pull request with a clear description of the change.
|
|
18
|
+
|
|
19
|
+
## Code Style
|
|
20
|
+
|
|
21
|
+
- **Indentation**: 2 spaces.
|
|
22
|
+
- **Semicolons**: Required.
|
|
23
|
+
- **Quotes**: Single quotes for strings.
|
|
24
|
+
- **Module system**: CommonJS (`require`/`module.exports`).
|
|
25
|
+
- **No external dependencies**: The core library must remain dependency-free. Development dependencies (linting, testing) are fine.
|
|
26
|
+
|
|
27
|
+
## Project Structure
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
src/ Core library (zero dependencies)
|
|
31
|
+
tests/ Test suite (Node.js built-in test runner)
|
|
32
|
+
demo-app/ Interactive demo application
|
|
33
|
+
test-server/ Dual-stack test server for manual testing
|
|
34
|
+
examples/ Usage examples
|
|
35
|
+
docs/ Extended documentation
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Testing
|
|
39
|
+
|
|
40
|
+
We use the Node.js built-in test runner:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
When adding a new feature or fixing a bug:
|
|
47
|
+
|
|
48
|
+
- Add tests that cover the new behavior.
|
|
49
|
+
- Tests must be deterministic and must not depend on network connectivity.
|
|
50
|
+
Spin up a local server on an ephemeral port instead — `tests/helpers.js` has
|
|
51
|
+
helpers for HTTP and TCP servers, raw requests and module reloading.
|
|
52
|
+
- Prefer a test that exercises the real path end to end over one that asserts a
|
|
53
|
+
function exists. The proxy shipped a release where every plain-HTTP request
|
|
54
|
+
returned `500` while the entire suite passed, because no test ever sent one.
|
|
55
|
+
- Configuration is captured when a module loads, so a test that changes
|
|
56
|
+
environment variables must call `reloadModules()` before requiring anything.
|
|
57
|
+
|
|
58
|
+
### Verifying protocol behavior
|
|
59
|
+
|
|
60
|
+
The proxy is easiest to verify with a real client:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
FORCE_BRIDGE=1 node src/cli.js start &
|
|
64
|
+
curl -x http://127.0.0.1:8080 http://example.com/ # plain HTTP
|
|
65
|
+
curl -x http://127.0.0.1:8080 https://example.com/ # CONNECT tunnel
|
|
66
|
+
curl --socks5-hostname 127.0.0.1:1080 https://example.com/
|
|
67
|
+
curl http://127.0.0.1:8080/status
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Note that proxy clients send *absolute-form* request targets, which is different
|
|
71
|
+
from how a normal HTTP server is addressed. Reading the code is not enough to
|
|
72
|
+
confirm proxy behavior — drive it with a client.
|
|
73
|
+
|
|
74
|
+
## Standards
|
|
75
|
+
|
|
76
|
+
Changes to address handling should cite the relevant RFC and, where the RFC
|
|
77
|
+
provides test vectors, use them. `tests/ipv6.test.js` checks the RFC 6052
|
|
78
|
+
section 2.4 vectors directly.
|
|
79
|
+
|
|
80
|
+
## Reporting Issues
|
|
81
|
+
|
|
82
|
+
- Search existing issues before creating a new one.
|
|
83
|
+
- Include your Node.js version, operating system, and steps to reproduce.
|
|
84
|
+
- If relevant, include the output of `npx ipv6-bridge --help`.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|