@@ -30,7 +30,7 @@ require ( | |||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect | github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect | ||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect | ||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect | github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 | |||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible | github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | ||||
github.com/emirpasic/gods v1.12.0 | github.com/emirpasic/gods v1.12.0 | ||||
@@ -96,7 +96,7 @@ require ( | |||||
github.com/urfave/cli v1.20.0 | github.com/urfave/cli v1.20.0 | ||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 | github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 | ||||
go.etcd.io/bbolt v1.3.3 // indirect | go.etcd.io/bbolt v1.3.3 // indirect | ||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 | |||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 | |||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 | golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 | ||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | ||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 | golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 | ||||
@@ -122,6 +122,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= | github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 h1:bpWCJ5MddHsv4Xtl3azkK89mZzd/vvut32mvAnKbyUA= | github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 h1:bpWCJ5MddHsv4Xtl3azkK89mZzd/vvut32mvAnKbyUA= | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA= | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | |||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | ||||
@@ -577,8 +579,8 @@ golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8U | |||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ= | golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ= | ||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= | |||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= | |||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
@@ -56,7 +56,7 @@ Other supported formats are listed below. | |||||
* `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host. | * `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host. | ||||
* `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port. | * `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port. | ||||
* `Workstation ID` - The workstation name (default is the host name) | * `Workstation ID` - The workstation name (default is the host name) | ||||
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. | |||||
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`. | |||||
### The connection string can be specified in one of three formats: | ### The connection string can be specified in one of three formats: | ||||
@@ -11,6 +11,8 @@ import ( | |||||
"unicode" | "unicode" | ||||
) | ) | ||||
const defaultServerPort = 1433 | |||||
type connectParams struct { | type connectParams struct { | ||||
logFlags uint64 | logFlags uint64 | ||||
port uint64 | port uint64 | ||||
@@ -78,7 +80,7 @@ func parseConnectParams(dsn string) (connectParams, error) { | |||||
p.user = params["user id"] | p.user = params["user id"] | ||||
p.password = params["password"] | p.password = params["password"] | ||||
p.port = 1433 | |||||
p.port = 0 | |||||
strport, ok := params["port"] | strport, ok := params["port"] | ||||
if ok { | if ok { | ||||
var err error | var err error | ||||
@@ -183,7 +185,7 @@ func parseConnectParams(dsn string) (connectParams, error) { | |||||
if ok { | if ok { | ||||
p.serverSPN = serverSPN | p.serverSPN = serverSPN | ||||
} else { | } else { | ||||
p.serverSPN = fmt.Sprintf("MSSQLSvc/%s:%d", p.host, p.port) | |||||
p.serverSPN = generateSpn(p.host, resolveServerPort(p.port)) | |||||
} | } | ||||
workstation, ok := params["workstation id"] | workstation, ok := params["workstation id"] | ||||
@@ -205,6 +207,9 @@ func parseConnectParams(dsn string) (connectParams, error) { | |||||
appintent, ok := params["applicationintent"] | appintent, ok := params["applicationintent"] | ||||
if ok { | if ok { | ||||
if appintent == "ReadOnly" { | if appintent == "ReadOnly" { | ||||
if p.database == "" { | |||||
return p, fmt.Errorf("Database must be specified when ApplicationIntent is ReadOnly") | |||||
} | |||||
p.typeFlags |= fReadOnlyIntent | p.typeFlags |= fReadOnlyIntent | ||||
} | } | ||||
} | } | ||||
@@ -451,3 +456,15 @@ func splitConnectionStringOdbc(dsn string) (map[string]string, error) { | |||||
func normalizeOdbcKey(s string) string { | func normalizeOdbcKey(s string) string { | ||||
return strings.ToLower(strings.TrimRightFunc(s, unicode.IsSpace)) | return strings.ToLower(strings.TrimRightFunc(s, unicode.IsSpace)) | ||||
} | } | ||||
func resolveServerPort(port uint64) uint64 { | |||||
if port == 0 { | |||||
return defaultServerPort | |||||
} | |||||
return port | |||||
} | |||||
func generateSpn(host string, port uint64) string { | |||||
return fmt.Sprintf("MSSQLSvc/%s:%d", host, port) | |||||
} |
@@ -613,11 +613,13 @@ loop: | |||||
break loop | break loop | ||||
case doneStruct: | case doneStruct: | ||||
if token.isError() { | if token.isError() { | ||||
cancel() | |||||
return nil, s.c.checkBadConn(token.getError()) | return nil, s.c.checkBadConn(token.getError()) | ||||
} | } | ||||
case ReturnStatus: | case ReturnStatus: | ||||
s.c.setReturnStatus(token) | s.c.setReturnStatus(token) | ||||
case error: | case error: | ||||
cancel() | |||||
return nil, s.c.checkBadConn(token) | return nil, s.c.checkBadConn(token) | ||||
} | } | ||||
} | } | ||||
@@ -666,14 +666,14 @@ func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn ne | |||||
} | } | ||||
if len(ips) == 1 { | if len(ips) == 1 { | ||||
d := c.getDialer(&p) | d := c.getDialer(&p) | ||||
addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(p.port))) | |||||
addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(resolveServerPort(p.port)))) | |||||
conn, err = d.DialContext(ctx, "tcp", addr) | conn, err = d.DialContext(ctx, "tcp", addr) | ||||
} else { | } else { | ||||
//Try Dials in parallel to avoid waiting for timeouts. | //Try Dials in parallel to avoid waiting for timeouts. | ||||
connChan := make(chan net.Conn, len(ips)) | connChan := make(chan net.Conn, len(ips)) | ||||
errChan := make(chan error, len(ips)) | errChan := make(chan error, len(ips)) | ||||
portStr := strconv.Itoa(int(p.port)) | |||||
portStr := strconv.Itoa(int(resolveServerPort(p.port))) | |||||
for _, ip := range ips { | for _, ip := range ips { | ||||
go func(ip net.IP) { | go func(ip net.IP) { | ||||
d := c.getDialer(&p) | d := c.getDialer(&p) | ||||
@@ -711,7 +711,7 @@ func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn ne | |||||
// Can't do the usual err != nil check, as it is possible to have gotten an error before a successful connection | // Can't do the usual err != nil check, as it is possible to have gotten an error before a successful connection | ||||
if conn == nil { | if conn == nil { | ||||
f := "Unable to open tcp connection with host '%v:%v': %v" | f := "Unable to open tcp connection with host '%v:%v': %v" | ||||
return nil, fmt.Errorf(f, p.host, p.port, err.Error()) | |||||
return nil, fmt.Errorf(f, p.host, resolveServerPort(p.port), err.Error()) | |||||
} | } | ||||
return conn, err | return conn, err | ||||
} | } | ||||
@@ -724,7 +724,7 @@ func connect(ctx context.Context, c *Connector, log optionalLogger, p connectPar | |||||
defer cancel() | defer cancel() | ||||
} | } | ||||
// if instance is specified use instance resolution service | // if instance is specified use instance resolution service | ||||
if p.instance != "" { | |||||
if p.instance != "" && p.port == 0 { | |||||
p.instance = strings.ToUpper(p.instance) | p.instance = strings.ToUpper(p.instance) | ||||
d := c.getDialer(&p) | d := c.getDialer(&p) | ||||
instances, err := getInstances(dialCtx, d, p.host) | instances, err := getInstances(dialCtx, d, p.host) | ||||
@@ -737,11 +737,12 @@ func connect(ctx context.Context, c *Connector, log optionalLogger, p connectPar | |||||
f := "No instance matching '%v' returned from host '%v'" | f := "No instance matching '%v' returned from host '%v'" | ||||
return nil, fmt.Errorf(f, p.instance, p.host) | return nil, fmt.Errorf(f, p.instance, p.host) | ||||
} | } | ||||
p.port, err = strconv.ParseUint(strport, 0, 16) | |||||
port, err := strconv.ParseUint(strport, 0, 16) | |||||
if err != nil { | if err != nil { | ||||
f := "Invalid tcp port returned from Sql Server Browser '%v': %v" | f := "Invalid tcp port returned from Sql Server Browser '%v': %v" | ||||
return nil, fmt.Errorf(f, strport, err.Error()) | return nil, fmt.Errorf(f, strport, err.Error()) | ||||
} | } | ||||
p.port = port | |||||
} | } | ||||
initiate_connection: | initiate_connection: | ||||
@@ -114,7 +114,7 @@ github.com/couchbase/vellum/utf8 | |||||
github.com/couchbaselabs/go-couchbase | github.com/couchbaselabs/go-couchbase | ||||
# github.com/davecgh/go-spew v1.1.1 | # github.com/davecgh/go-spew v1.1.1 | ||||
github.com/davecgh/go-spew/spew | github.com/davecgh/go-spew/spew | ||||
# github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 | |||||
# github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 | |||||
github.com/denisenkom/go-mssqldb | github.com/denisenkom/go-mssqldb | ||||
github.com/denisenkom/go-mssqldb/internal/cp | github.com/denisenkom/go-mssqldb/internal/cp | ||||
github.com/denisenkom/go-mssqldb/internal/decimal | github.com/denisenkom/go-mssqldb/internal/decimal | ||||
@@ -439,7 +439,7 @@ go.mongodb.org/mongo-driver/bson/bsonrw | |||||
go.mongodb.org/mongo-driver/bson/bsontype | go.mongodb.org/mongo-driver/bson/bsontype | ||||
go.mongodb.org/mongo-driver/bson/primitive | go.mongodb.org/mongo-driver/bson/primitive | ||||
go.mongodb.org/mongo-driver/x/bsonx/bsoncore | go.mongodb.org/mongo-driver/x/bsonx/bsoncore | ||||
# golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 | |||||
# golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 | |||||
golang.org/x/crypto/acme | golang.org/x/crypto/acme | ||||
golang.org/x/crypto/acme/autocert | golang.org/x/crypto/acme/autocert | ||||
golang.org/x/crypto/argon2 | golang.org/x/crypto/argon2 | ||||