Pull request: 3418-clientid-doh
Closes #3418. Squashed commit of the following: commit 8a1180f8ef03d30ea3ae6a3e3121ddcac513f45b Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Wed Oct 5 17:26:22 2022 +0300 all: imp docs, tests commit 9629c69b39540db119044f2f79c1c4ed39de911f Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Wed Oct 5 15:34:33 2022 +0300 dnsforward: accept clientids from doh client srvname
This commit is contained in:
parent
2e0f6e5468
commit
330ac30324
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -15,6 +15,16 @@ and this project adheres to
|
||||||
## [v0.108.0] - TBA (APPROX.)
|
## [v0.108.0] - TBA (APPROX.)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- The ability to put [ClientIDs][clientid] into DNS-over-HTTPS hostnames as
|
||||||
|
opposed to URL paths ([#3418]). Note that AdGuard Home checks the server name
|
||||||
|
only if the URL does not contain a ClientID.
|
||||||
|
|
||||||
|
[#3418]: https://github.com/AdguardTeam/AdGuardHome/issues/3418
|
||||||
|
|
||||||
|
[clientid]: https://github.com/AdguardTeam/AdGuardHome/wiki/Clients#clientid
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -123,7 +123,14 @@ type quicConnection interface {
|
||||||
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
|
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
|
||||||
proto := pctx.Proto
|
proto := pctx.Proto
|
||||||
if proto == proxy.ProtoHTTPS {
|
if proto == proxy.ProtoHTTPS {
|
||||||
return clientIDFromDNSContextHTTPS(pctx)
|
clientID, err = clientIDFromDNSContextHTTPS(pctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("checking url: %w", err)
|
||||||
|
} else if clientID != "" {
|
||||||
|
return clientID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go on and check the domain name as well.
|
||||||
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
|
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
@ -133,31 +140,9 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cliSrvName := ""
|
cliSrvName, err := clientServerName(pctx, proto)
|
||||||
switch proto {
|
if err != nil {
|
||||||
case proxy.ProtoTLS:
|
return "", err
|
||||||
conn := pctx.Conn
|
|
||||||
tc, ok := conn.(tlsConn)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf(
|
|
||||||
"proxy ctx conn of proto %s is %T, want *tls.Conn",
|
|
||||||
proto,
|
|
||||||
conn,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cliSrvName = tc.ConnectionState().ServerName
|
|
||||||
case proxy.ProtoQUIC:
|
|
||||||
conn, ok := pctx.QUICConnection.(quicConnection)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf(
|
|
||||||
"proxy ctx quic conn of proto %s is %T, want quic.Connection",
|
|
||||||
proto,
|
|
||||||
pctx.QUICConnection,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cliSrvName = conn.ConnectionState().TLS.ServerName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clientID, err = clientIDFromClientServerName(
|
clientID, err = clientIDFromClientServerName(
|
||||||
|
@ -171,3 +156,35 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
|
||||||
|
|
||||||
return clientID, nil
|
return clientID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clientServerName returns the TLS server name based on the protocol.
|
||||||
|
func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string, err error) {
|
||||||
|
switch proto {
|
||||||
|
case proxy.ProtoHTTPS:
|
||||||
|
if connState := pctx.HTTPRequest.TLS; connState != nil {
|
||||||
|
srvName = pctx.HTTPRequest.TLS.ServerName
|
||||||
|
}
|
||||||
|
case proxy.ProtoQUIC:
|
||||||
|
qConn := pctx.QUICConnection
|
||||||
|
conn, ok := qConn.(quicConnection)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"proxy ctx quic conn of proto %s is %T, want quic.Connection",
|
||||||
|
proto,
|
||||||
|
qConn,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
srvName = conn.ConnectionState().TLS.ServerName
|
||||||
|
case proxy.ProtoTLS:
|
||||||
|
conn := pctx.Conn
|
||||||
|
tc, ok := conn.(tlsConn)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("proxy ctx conn of proto %s is %T, want *tls.Conn", proto, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
srvName = tc.ConnectionState().ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
return srvName, nil
|
||||||
|
}
|
||||||
|
|
|
@ -160,6 +160,22 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||||
wantClientID: "insensitive",
|
wantClientID: "insensitive",
|
||||||
wantErrMsg: ``,
|
wantErrMsg: ``,
|
||||||
strictSNI: true,
|
strictSNI: true,
|
||||||
|
}, {
|
||||||
|
name: "https_no_clientid",
|
||||||
|
proto: proxy.ProtoHTTPS,
|
||||||
|
hostSrvName: "example.com",
|
||||||
|
cliSrvName: "example.com",
|
||||||
|
wantClientID: "",
|
||||||
|
wantErrMsg: "",
|
||||||
|
strictSNI: true,
|
||||||
|
}, {
|
||||||
|
name: "https_clientid",
|
||||||
|
proto: proxy.ProtoHTTPS,
|
||||||
|
hostSrvName: "example.com",
|
||||||
|
cliSrvName: "cli.example.com",
|
||||||
|
wantClientID: "cli",
|
||||||
|
wantErrMsg: "",
|
||||||
|
strictSNI: true,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -173,16 +189,32 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||||
conf: ServerConfig{TLSConfig: tlsConf},
|
conf: ServerConfig{TLSConfig: tlsConf},
|
||||||
}
|
}
|
||||||
|
|
||||||
var conn net.Conn
|
var (
|
||||||
if tc.proto == proxy.ProtoTLS {
|
conn net.Conn
|
||||||
conn = testTLSConn{
|
qconn quic.Connection
|
||||||
|
httpReq *http.Request
|
||||||
|
)
|
||||||
|
|
||||||
|
switch tc.proto {
|
||||||
|
case proxy.ProtoHTTPS:
|
||||||
|
u := &url.URL{
|
||||||
|
Path: "/dns-query",
|
||||||
|
}
|
||||||
|
|
||||||
|
connState := &tls.ConnectionState{
|
||||||
|
ServerName: tc.cliSrvName,
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq = &http.Request{
|
||||||
|
URL: u,
|
||||||
|
TLS: connState,
|
||||||
|
}
|
||||||
|
case proxy.ProtoQUIC:
|
||||||
|
qconn = testQUICConnection{
|
||||||
serverName: tc.cliSrvName,
|
serverName: tc.cliSrvName,
|
||||||
}
|
}
|
||||||
}
|
case proxy.ProtoTLS:
|
||||||
|
conn = testTLSConn{
|
||||||
var qconn quic.Connection
|
|
||||||
if tc.proto == proxy.ProtoQUIC {
|
|
||||||
qconn = testQUICConnection{
|
|
||||||
serverName: tc.cliSrvName,
|
serverName: tc.cliSrvName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,6 +222,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||||
pctx := &proxy.DNSContext{
|
pctx := &proxy.DNSContext{
|
||||||
Proto: tc.proto,
|
Proto: tc.proto,
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
|
HTTPRequest: httpReq,
|
||||||
QUICConnection: qconn,
|
QUICConnection: qconn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,56 +238,76 @@ func TestClientIDFromDNSContextHTTPS(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
path string
|
path string
|
||||||
|
cliSrvName string
|
||||||
wantClientID string
|
wantClientID string
|
||||||
wantErrMsg string
|
wantErrMsg string
|
||||||
}{{
|
}{{
|
||||||
name: "no_clientid",
|
name: "no_clientid",
|
||||||
path: "/dns-query",
|
path: "/dns-query",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "",
|
wantClientID: "",
|
||||||
wantErrMsg: "",
|
wantErrMsg: "",
|
||||||
}, {
|
}, {
|
||||||
name: "no_clientid_slash",
|
name: "no_clientid_slash",
|
||||||
path: "/dns-query/",
|
path: "/dns-query/",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "",
|
wantClientID: "",
|
||||||
wantErrMsg: "",
|
wantErrMsg: "",
|
||||||
}, {
|
}, {
|
||||||
name: "clientid",
|
name: "clientid",
|
||||||
path: "/dns-query/cli",
|
path: "/dns-query/cli",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "cli",
|
wantClientID: "cli",
|
||||||
wantErrMsg: "",
|
wantErrMsg: "",
|
||||||
}, {
|
}, {
|
||||||
name: "clientid_slash",
|
name: "clientid_slash",
|
||||||
path: "/dns-query/cli/",
|
path: "/dns-query/cli/",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "cli",
|
wantClientID: "cli",
|
||||||
wantErrMsg: "",
|
wantErrMsg: "",
|
||||||
}, {
|
}, {
|
||||||
name: "clientid_case",
|
name: "clientid_case",
|
||||||
path: "/dns-query/InSeNsItIvE",
|
path: "/dns-query/InSeNsItIvE",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "insensitive",
|
wantClientID: "insensitive",
|
||||||
wantErrMsg: ``,
|
wantErrMsg: ``,
|
||||||
}, {
|
}, {
|
||||||
name: "bad_url",
|
name: "bad_url",
|
||||||
path: "/foo",
|
path: "/foo",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "",
|
wantClientID: "",
|
||||||
wantErrMsg: `clientid check: invalid path "/foo"`,
|
wantErrMsg: `clientid check: invalid path "/foo"`,
|
||||||
}, {
|
}, {
|
||||||
name: "extra",
|
name: "extra",
|
||||||
path: "/dns-query/cli/foo",
|
path: "/dns-query/cli/foo",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "",
|
wantClientID: "",
|
||||||
wantErrMsg: `clientid check: invalid path "/dns-query/cli/foo": extra parts`,
|
wantErrMsg: `clientid check: invalid path "/dns-query/cli/foo": extra parts`,
|
||||||
}, {
|
}, {
|
||||||
name: "invalid_clientid",
|
name: "invalid_clientid",
|
||||||
path: "/dns-query/!!!",
|
path: "/dns-query/!!!",
|
||||||
|
cliSrvName: "example.com",
|
||||||
wantClientID: "",
|
wantClientID: "",
|
||||||
wantErrMsg: `clientid check: invalid clientid "!!!": bad domain name label rune '!'`,
|
wantErrMsg: `clientid check: invalid clientid "!!!": bad domain name label rune '!'`,
|
||||||
|
}, {
|
||||||
|
name: "both_ids",
|
||||||
|
path: "/dns-query/right",
|
||||||
|
cliSrvName: "wrong.example.com",
|
||||||
|
wantClientID: "right",
|
||||||
|
wantErrMsg: "",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
connState := &tls.ConnectionState{
|
||||||
|
ServerName: tc.cliSrvName,
|
||||||
|
}
|
||||||
|
|
||||||
r := &http.Request{
|
r := &http.Request{
|
||||||
URL: &url.URL{
|
URL: &url.URL{
|
||||||
Path: tc.path,
|
Path: tc.path,
|
||||||
},
|
},
|
||||||
|
TLS: connState,
|
||||||
}
|
}
|
||||||
|
|
||||||
pctx := &proxy.DNSContext{
|
pctx := &proxy.DNSContext{
|
||||||
|
|
Loading…
Reference in New Issue