masque-go is an implementation of the CONNECT-UDP protocol RFC 9298, based on quic-go.
quic-go / masque-go Goto Github PK
View Code? Open in Web Editor NEWMASQUE: Proxying UDP in HTTP/3, RFC 9298
License: MIT License
MASQUE: Proxying UDP in HTTP/3, RFC 9298
License: MIT License
A proxy might want to control DNS resolution of target hosts (e.g. force usage of DoH). This is currently not possible, since the proxy implementation handles DNS resolution.
We could combine pass the hostname to DialTarget
instead:
type Proxy struct {
// DialTarget is called when the proxy needs to open a new UDP socket to the target server.
// It must return a connected UDP socket.
// TODO(#3): support unconnected sockets.
DialTarget func(context.Context, string) (*net.UDPConn, error)
}
This would also allow us to remove the Allow
callback.
It's not clear how we should set the HTTP status code though.
That would be a more accurate naming, wouldn't it?
The URL should is already encoded in the URI template. Unfortunately, it doesn't seem like the URI template library allows us to extract it though, so we might need to jump through some hoops to obtain it.
This will likely improve performance on high-bandwidth connections.
We should probably refactor quite a bit of quic-go's GSO code into a separate package (repo?), so we don't have to build all the platform-specific code again.
The client might be interested in HTTP headers set by the server, for example the RFC 9209 proxy status headers (see #2), and the HTTP status code, in case something went wrong.
quic-go currently uses a hard-coded initial MTU. This means that we currently can't proxy QUIC traffic, since the MTU of the outer and the inner connection are the same, and therefore the DATAGRAM frames don't fit into the QUIC packets.
There are a number of issues highlighting deficiencies with the current proxy API: #3, #4, #13, #41. There's a lot of back-and-forth between the request handler and the application, if we want to enable the fine-grained access control that's table stakes for a production-ready proxy.
The reason that implementing a HTTP CONNECT proxy is so much easier is because the http.Request
contains all fields that are needed to make the proxying decison.
We could replicate this by implementing a ParseRequest
function on the Proxy
:
type Proxy struct {
// Template is the URI template that clients will use to configure this UDP proxy.
Template *uritemplate.Template
}
func (p *Proxy) ParseRequest(*http.Request) (*Request, error) {
// parse the request using the configure URI template
}
Borrowing the idea from #41 (comment), the returned error could implement a ParseError
, allowing the application to set the correct HTTP status code (while also allowing it to ignore the suggestion and set their own status):
type ParsingError struct {
HTTPStatus int
Error string
}
func (e *ParsingError) Error() string { return e.Error }
After ParseRequest
, it's the application's responsibility to decide how to proceed with the request. In case it wishes to continue, it needs to pass the masque.Request
back to the Proxy
, together with a connection to the target host:
func (p *Proxy) HandleRequest(*Request, *net.UDPConn) error {
// proxy packets back and forth
}
Applications might want to know which client they're establishing a connection for, for example.
Then using this particular URI template library would remain an implementation detail.
It dials a net.UDPAddr
, not just an IP.
Blocked on quic-go/quic-go#4505.
A proxy MUST NOT introduce fragmentation at the IP layer.
Currently the proxy uses connected sockets. This is a good idea performance-wise, but comes with the obvious limitation on the number of proxied connections.
We should have an API that allows an application more fine-grained control. One option would be a callback on the Server
:
type Server struct {
// ... existing stuff
// PacketConnForRemote is called when establishing a new proxied connection.
// It is possible to return the same net.PacketConn (an unconnected UDP socket) for multiple distinct remote address.
// However, the same net.PacketConn cannot be used for the same remote address.
PacketConnForRemote(*net.UDPAddr) net.PacketConn
}
The problem here is that the same net.PacketConn
can't be used for the same remote address: We need to know which QUIC connection to put a packet on. It's also not clear how timeouts should work: If one proxied connection is closed, it should be possible to reuse the same net.PacketConn
at some point, but probably not immediately, since UDP packets might still be in flight between the remote and the proxy.
This will be a lot easier to implement than in quic-go (quic-go/quic-go#3838), since we only need to do a minimal amount of processing per packet.
We need more control about our CI setup (see #7 (comment) for example). We should set up our own CI config here.
It's advantageous to leave DNS resolution to the proxy. This has privacy benefits, and might DNS resolution might end up at a closer server (in case of GeoDNS).
The current Allow
callback on the Server
is called after DNS resolution. We should probably have a way to skip DNS resolution if we dislike the hostname.
RFC 9209 defines how a proxy can communicate all kinds of failure scenarios.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.