From 8b2e5417314413bc95cf4ee5a1c98bfb319dfc75 Mon Sep 17 00:00:00 2001 From: mpl Date: Wed, 31 May 2017 17:30:36 +0200 Subject: [PATCH] pkg/client: set ServerName before DialTLS handshake for android Otherwise, the android app fails to connect with a server that uses Let's Encrypt (because it relies on SNI, which requires the ServerName to be set). Change-Id: I9f25486bea68e83c68584a83817c98bfc84f62b9 --- pkg/client/client.go | 30 +++++++++++++++++++++++++++++- pkg/client/config_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index a142925eb..da4ec260d 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -333,6 +333,12 @@ func (o optionSameOrigin) modifyClient(c *Client) { c.sameOrigin = bool(o) } +type optionParamsOnly bool + +func (o optionParamsOnly) modifyClient(c *Client) { + c.paramsOnly = bool(o) +} + // noop is for use with syncutil.Onces. func noop() error { return nil } @@ -1149,6 +1155,10 @@ func (c *Client) DialTLSFunc() func(network, addr string) (net.Conn, error) { } else { tlsConfig = &tls.Config{InsecureSkipVerify: true} } + // Since we're doing the TLS handshake ourselves, we need to set the ServerName, + // in case the server uses SNI (as is the case if it's relying on Let's Encrypt, + // for example). + tlsConfig.ServerName = c.serverNameOfAddr(addr) conn = tls.Client(ac, tlsConfig) if err := conn.Handshake(); err != nil { return nil, err @@ -1176,6 +1186,21 @@ func (c *Client) DialTLSFunc() func(network, addr string) (net.Conn, error) { } } +// serverNameOfAddr returns the host part of addr, or the empty string if addr +// is not a valid address (see net.Dial). Additionally, if host is an IP literal, +// serverNameOfAddr returns the empty string. +func (c *Client) serverNameOfAddr(addr string) string { + serverName, _, err := net.SplitHostPort(addr) + if err != nil { + c.printf("could not get server name from address %q: %v", addr, err) + return "" + } + if ip := net.ParseIP(serverName); ip != nil { + return "" + } + return serverName +} + func (c *Client) Signer() (*schema.Signer, error) { c.signerOnce.Do(c.signerInit) return c.signer, c.signerErr @@ -1299,8 +1324,11 @@ func (c *Client) Close() error { // and auth but does not use any on-disk config files or environment variables // for its configuration. It may still use the disk for caches. func NewFromParams(server string, mode auth.AuthMode, opts ...ClientOption) *Client { + // paramsOnly = true needs to be passed as soon as an argument, because + // there are code paths in newClient (c.transportForConfig) that can lead + // to parsing the config file. + opts = append(opts[:len(opts):len(opts)], optionParamsOnly(true)) cl := newClient(server, mode, opts...) - cl.paramsOnly = true return cl } diff --git a/pkg/client/config_test.go b/pkg/client/config_test.go index 9b7f695da..4591cf52b 100644 --- a/pkg/client/config_test.go +++ b/pkg/client/config_test.go @@ -61,3 +61,29 @@ func TestAliasFromConfig(t *testing.T) { } } } + +func TestServerOfName(t *testing.T) { + addrs := []struct { + input string + want string + }{ + { + input: "foo.com:80", + want: "foo.com", + }, + { + input: "192.168.0.9:80", + want: "", + }, + { + input: "foo.com", + want: "", + }, + } + c := NewFromParams("whatever", nil) + for _, v := range addrs { + if got := c.serverNameOfAddr(v.input); got != v.want { + t.Errorf("wanted %v, got %q", v.want, got) + } + } +}