Add a proxy protocol
The examples are module/proxy/socks5 and module/proxy/http. A new stream proxy touches:
| File | Change |
|---|---|
module/proxy/<name>/settings.go (new) | settings, Validate, Warnings |
module/proxy/<name>/proxy.go (new) | Proxy type, Info, NewStreamServer, NewStreamClient, NewProxyModule |
module/proxy/<name>/server.go, client.go (new) | the two roles |
module/proxy/<name>/wire/ (new, optional) | the byte format, kept separate so it can be tested and fuzzed alone |
module/proxy/<name>/<name>_test.go (new) | tests over net.Pipe, no core |
core/registry/default.go | one line: RegisterStream(<name>.NewProxyModule) |
Rules
- Server.
AcceptStream(ctx, underlyingStream)reads the request and returns the payload stream andspec.Metadatawith the destination, withinctx. - Client.
RequestStream(ctx, underlyingStream, metadata)writes the request onto a stream that already reaches the server, or holds it for the first write. - Never dial. The stream is handed to you. A protocol that truly needs a second socket is a design question: raise it first.
- Fallback. For a request that is not your protocol, return
spec.ErrFallbackRequestedbefore writing anything; core replays it to the inbound's fallback. - Wrong credentials. Wrap
spec.ErrAuthenticationFailed, so core counts them apart from garbage.
Users
If clients log in, implement spec.UserManager (AddUser, RemoveUser, ListUsers) on the server; module/proxy/trojan is the example.
- A user is one element of your
userslist, decoded with unknown keys refused. - Return
spec.ErrInvalidUser,spec.ErrUserExistsorspec.ErrNoSuchUser, with no credential in the message. - Keep users in an immutable snapshot behind an
atomic.Pointer, and compare in constant time. - Refuse the other role's keys in
NewStreamServerandNewStreamClient, so a stray key cannot leave an inbound open.
UDP
A protocol with UDP also fills spec.DatagramOverStreamProxyModule (UDP framed in the stream) or spec.DatagramProxyModule. It stays one module: NewProxyModule returns the combination, such as spec.StreamAndDatagramOverStreamProxyModule, and is registered once with RegisterStreamAndDatagramOverStream.
- Build the server once, so both kinds share one object (
sync.Onceintrojan). - Implement
IsDatagramOverStream, which only peeks, so core can choose betweenAcceptStreamandAcceptDatagramOverStream.