diff --git a/namedpipe/namedpipe_windows.go b/namedpipe/namedpipe_windows.go index 24ea737e..9e2df884 100644 --- a/namedpipe/namedpipe_windows.go +++ b/namedpipe/namedpipe_windows.go @@ -28,26 +28,34 @@ func (n namedPipeDialer) ParseServer(server string, p *msdsn.Config) error { if strings.HasPrefix(server, `\\`) { // assume a server name starting with \\ is the full named pipe path p.ProtocolParameters[n.Protocol()] = namedPipeData{PipeName: server} - } else if p.Host == "" { // if the string specifies np:host\instance, tcpParser won't have filled in p.Host + return nil + } + pipeHost := "." + if p.Host == "" { // if the string specifies np:host\instance, tcpParser won't have filled in p.Host parts := strings.SplitN(server, `\`, 2) - p.Host = parts[0] - if p.Host == "." || strings.ToUpper(p.Host) == "(LOCAL)" { + host := parts[0] + if host == "." || strings.ToUpper(host) == "(LOCAL)" { + // localhost replaces . to query the browser service but some SQL instances + // like Windows Internal Database require the . in the pipe name to connect p.Host = "localhost" + } else { + p.Host = host + pipeHost = host } if len(parts) > 1 { p.Instance = parts[1] } } else { - host := strings.ToLower(p.Host) + pipeHost = strings.ToLower(p.Host) for _, domain := range azureDomains { - if strings.HasSuffix(host, domain) { + if strings.HasSuffix(pipeHost, domain) { return fmt.Errorf("Named pipes disallowed for Azure SQL Database connections") } } } pipe, ok := p.Parameters["pipe"] if ok { - p.ProtocolParameters[n.Protocol()] = namedPipeData{PipeName: fmt.Sprintf(`\\%s\pipe\%s`, p.Host, pipe)} + p.ProtocolParameters[n.Protocol()] = namedPipeData{PipeName: fmt.Sprintf(`\\%s\pipe\%s`, pipeHost, pipe)} } return nil } diff --git a/namedpipe/namepipe_windows_test.go b/namedpipe/namepipe_windows_test.go new file mode 100644 index 00000000..e6b0c5e3 --- /dev/null +++ b/namedpipe/namepipe_windows_test.go @@ -0,0 +1,62 @@ +package namedpipe + +import ( + "testing" + + "github.com/microsoft/go-mssqldb/msdsn" + "github.com/stretchr/testify/assert" +) + +func TestParseServer(t *testing.T) { + c := &msdsn.Config{ + Port: 1000, + } + n := &namedPipeDialer{} + err := n.ParseServer("server", c) + assert.Errorf(t, err, "ParseServer with a Port") + + c = &msdsn.Config{ + Parameters: make(map[string]string), + ProtocolParameters: make(map[string]interface{}), + } + err = n.ParseServer(`\\.\pipe\MSSQL$Instance\sql\query`, c) + assert.NoError(t, err, "ParseServer with a full pipe name") + assert.Equal(t, "", c.Host, "Config Host with a full pipe name") + data, ok := c.ProtocolParameters[n.Protocol()] + assert.True(t, ok, "Should have added ProtocolParameters when server is pipe name") + switch d := data.(type) { + case namedPipeData: + assert.Equal(t, `\\.\pipe\MSSQL$Instance\sql\query`, d.PipeName, "Pipe name in ProtocolParameters when server is pipe name") + default: + assert.Fail(t, "Incorrect protocol parameters type:", d) + } + + c = &msdsn.Config{ + Parameters: make(map[string]string), + ProtocolParameters: make(map[string]interface{}), + } + err = n.ParseServer(`.\instance`, c) + assert.NoError(t, err, "ParseServer .") + assert.Equal(t, "localhost", c.Host, `Config Host with server == .\instance`) + assert.Equal(t, "instance", c.Instance, `Config Instance with server == .\instance`) + _, ok = c.ProtocolParameters[n.Protocol()] + assert.Equal(t, ok, false, "Should have no namedPipeData when pipe name omitted") + + c = &msdsn.Config{ + Host: "server", + Parameters: make(map[string]string), + ProtocolParameters: make(map[string]interface{}), + } + c.Parameters["pipe"] = `myinstance\sql\query` + err = n.ParseServer(`anything`, c) + assert.NoError(t, err, "ParseServer anything") + data, ok = c.ProtocolParameters[n.Protocol()] + assert.True(t, ok, "Should have added ProtocolParameters when pipe name is provided") + switch d := data.(type) { + case namedPipeData: + assert.Equal(t, `\\server\pipe\myinstance\sql\query`, d.PipeName, "Pipe name in ProtocolParameters") + default: + assert.Fail(t, "Incorrect protocol parameters type:", d) + } + +}