Skip to content

Commit

Permalink
remove the template parameter from the client (#73)
Browse files Browse the repository at this point in the history
The same UDP proxy could offer proxying functionality at different
paths (and therefore different templates), and we should be able to
multiplex these on the same QUIC connection.
  • Loading branch information
marten-seemann authored Oct 17, 2024
1 parent 99f149a commit 62310b9
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 27 deletions.
20 changes: 5 additions & 15 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,9 @@ import (
// This allows tunneling QUIC connections, which themselves have a minimum MTU requirement of 1200 bytes.
const defaultInitialPacketSize = 1350

// A Client establishes proxied connections to remote hosts,
// using a UDP proxy.
// A Client establishes proxied connections to remote hosts, using a UDP proxy.
// Multiple flows can be proxied via the same connection to the proxy.
type Client struct {
// Template is the URI template of the UDP proxy.
Template *uritemplate.Template

// TLSClientConfig is the TLS client config used when dialing the QUIC connection to the proxy.
// It must set the "h3" ALPN.
TLSClientConfig *tls.Config
Expand All @@ -43,15 +39,12 @@ type Client struct {
// DialAddr dials a proxied connection to a target server.
// The target address is sent to the proxy, and the DNS resolution is left to the proxy.
// The target must be given as a host:port.
func (c *Client) DialAddr(ctx context.Context, target string) (net.PacketConn, *http.Response, error) {
if c.Template == nil {
return nil, nil, errors.New("masque: no template")
}
func (c *Client) DialAddr(ctx context.Context, proxyTemplate *uritemplate.Template, target string) (net.PacketConn, *http.Response, error) {
host, port, err := net.SplitHostPort(target)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse target: %w", err)
}
str, err := c.Template.Expand(uritemplate.Values{
str, err := proxyTemplate.Expand(uritemplate.Values{
uriTemplateTargetHost: uritemplate.String(host),
uriTemplateTargetPort: uritemplate.String(port),
})
Expand All @@ -62,11 +55,8 @@ func (c *Client) DialAddr(ctx context.Context, target string) (net.PacketConn, *
}

// Dial dials a proxied connection to a target server.
func (c *Client) Dial(ctx context.Context, raddr *net.UDPAddr) (net.PacketConn, *http.Response, error) {
if c.Template == nil {
return nil, nil, errors.New("masque: no template")
}
str, err := c.Template.Expand(uritemplate.Values{
func (c *Client) Dial(ctx context.Context, proxyTemplate *uritemplate.Template, raddr *net.UDPAddr) (net.PacketConn, *http.Response, error) {
str, err := proxyTemplate.Expand(uritemplate.Values{
uriTemplateTargetHost: uritemplate.String(escape(raddr.IP.String())),
uriTemplateTargetPort: uritemplate.String(strconv.Itoa(raddr.Port)),
})
Expand Down
3 changes: 1 addition & 2 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func main() {
}

cl := masque.Client{
Template: uritemplate.MustNew(proxyURITemplate),
QUICConfig: &quic.Config{
EnableDatagrams: true,
InitialPacketSize: 1350,
Expand All @@ -52,7 +51,7 @@ func main() {
if err != nil {
return nil, err
}
pconn, _, err := cl.Dial(context.Background(), raddr)
pconn, _, err := cl.Dial(context.Background(), uritemplate.MustNew(proxyURITemplate), raddr)
if err != nil {
log.Fatal("dialing MASQUE failed:", err)
}
Expand Down
23 changes: 13 additions & 10 deletions connect-udp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ func testProxyToIP(t *testing.T, addr *net.UDPAddr) {
}()

cl := masque.Client{
Template: template,
TLSClientConfig: &tls.Config{ClientCAs: certPool, NextProtos: []string{http3.NextProtoH3}, InsecureSkipVerify: true},
}
defer cl.Close()
proxiedConn, _, err := cl.Dial(context.Background(), remoteServerConn.LocalAddr().(*net.UDPAddr))
proxiedConn, _, err := cl.Dial(
context.Background(),
template,
remoteServerConn.LocalAddr().(*net.UDPAddr),
)
require.NoError(t, err)

_, err = proxiedConn.WriteTo([]byte("foobar"), remoteServerConn.LocalAddr())
Expand Down Expand Up @@ -141,11 +144,10 @@ func TestProxyToHostname(t *testing.T) {
}()

cl := masque.Client{
Template: template,
TLSClientConfig: &tls.Config{ClientCAs: certPool, NextProtos: []string{http3.NextProtoH3}, InsecureSkipVerify: true},
}
defer cl.Close()
proxiedConn, rsp, err := cl.DialAddr(context.Background(), "quic-go.net:1234") // the proxy doesn't actually resolve this hostname
proxiedConn, rsp, err := cl.DialAddr(context.Background(), template, "quic-go.net:1234") // the proxy doesn't actually resolve this hostname
require.NoError(t, err)
require.Equal(t, http.StatusOK, rsp.StatusCode)

Expand Down Expand Up @@ -181,22 +183,24 @@ func TestProxyingRejected(t *testing.T) {
}()

cl := masque.Client{
Template: template,
TLSClientConfig: &tls.Config{ClientCAs: certPool, NextProtos: []string{http3.NextProtoH3}, InsecureSkipVerify: true},
}
defer cl.Close()
_, rsp, err := cl.DialAddr(context.Background(), "quic-go.net:1234") // the proxy doesn't actually resolve this hostname
_, rsp, err := cl.DialAddr(context.Background(), template, "quic-go.net:1234") // the proxy doesn't actually resolve this hostname
require.Error(t, err)
require.Equal(t, http.StatusTeapot, rsp.StatusCode)
}

func TestProxyToHostnameMissingPort(t *testing.T) {
cl := masque.Client{
Template: uritemplate.MustNew("https://localhost:1234/masque?h={target_host}&p={target_port}"),
TLSClientConfig: &tls.Config{ClientCAs: certPool, NextProtos: []string{http3.NextProtoH3}, InsecureSkipVerify: true},
}
defer cl.Close()
_, rsp, err := cl.DialAddr(context.Background(), "quic-go.net") // missing port
_, rsp, err := cl.DialAddr(
context.Background(),
uritemplate.MustNew("https://localhost:1234/masque?h={target_host}&p={target_port}"),
"quic-go.net", // missing port
)
require.Nil(t, rsp)
require.ErrorContains(t, err, "address quic-go.net: missing port in address")
}
Expand Down Expand Up @@ -236,11 +240,10 @@ func TestProxyShutdown(t *testing.T) {
}()

cl := masque.Client{
Template: template,
TLSClientConfig: &tls.Config{ClientCAs: certPool, NextProtos: []string{http3.NextProtoH3}, InsecureSkipVerify: true},
}
defer cl.Close()
proxiedConn, rsp, err := cl.Dial(context.Background(), remoteServerConn.LocalAddr().(*net.UDPAddr))
proxiedConn, rsp, err := cl.Dial(context.Background(), template, remoteServerConn.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
require.Equal(t, http.StatusOK, rsp.StatusCode)

Expand Down

0 comments on commit 62310b9

Please sign in to comment.