Skip to content

Commit 8b688fb

Browse files
committedJan 31, 2016
DSN: Add cfg.Format method
1 parent b4db83c commit 8b688fb

File tree

4 files changed

+278
-54
lines changed

4 files changed

+278
-54
lines changed
 

‎collations.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
package mysql
1010

11-
const defaultCollation byte = 33 // utf8_general_ci
11+
const defaultCollation = "utf8_general_ci"
1212

1313
// A list of available collations mapped to the internal ID.
1414
// To update this map use the following MySQL query:
@@ -237,14 +237,14 @@ var collations = map[string]byte{
237237

238238
// A blacklist of collations which is unsafe to interpolate parameters.
239239
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
240-
var unsafeCollations = map[byte]bool{
241-
1: true, // big5_chinese_ci
242-
13: true, // sjis_japanese_ci
243-
28: true, // gbk_chinese_ci
244-
84: true, // big5_bin
245-
86: true, // gb2312_bin
246-
87: true, // gbk_bin
247-
88: true, // sjis_bin
248-
95: true, // cp932_japanese_ci
249-
96: true, // cp932_bin
240+
var unsafeCollations = map[string]bool{
241+
"big5_chinese_ci": true,
242+
"sjis_japanese_ci": true,
243+
"gbk_chinese_ci": true,
244+
"big5_bin": true,
245+
"gb2312_bin": true,
246+
"gbk_bin": true,
247+
"sjis_bin": true,
248+
"cp932_japanese_ci": true,
249+
"cp932_bin": true,
250250
}

‎dsn.go

+209-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package mysql
1010

1111
import (
12+
"bytes"
1213
"crypto/tls"
1314
"errors"
1415
"fmt"
@@ -33,12 +34,13 @@ type Config struct {
3334
Addr string // Network address
3435
DBName string // Database name
3536
Params map[string]string // Connection parameters
37+
Collation string // Connection collation
3638
Loc *time.Location // Location for time.Time values
37-
TLS *tls.Config // TLS configuration
39+
TLSConfig string // TLS configuration name
40+
tls *tls.Config // TLS configuration
3841
Timeout time.Duration // Dial timeout
3942
ReadTimeout time.Duration // I/O read timeout
4043
WriteTimeout time.Duration // I/O write timeout
41-
Collation uint8 // Connection collation
4244

4345
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
4446
AllowCleartextPasswords bool // Allows the cleartext client side plugin
@@ -51,6 +53,194 @@ type Config struct {
5153
Strict bool // Return warnings as errors
5254
}
5355

56+
// FormatDSN formats the given Config into a DSN string which can be passed to
57+
// the driver.
58+
func (cfg *Config) FormatDSN() string {
59+
var buf bytes.Buffer
60+
61+
// [username[:password]@]
62+
if len(cfg.User) > 0 {
63+
buf.WriteString(cfg.User)
64+
if len(cfg.Passwd) > 0 {
65+
buf.WriteByte(':')
66+
buf.WriteString(cfg.Passwd)
67+
}
68+
buf.WriteByte('@')
69+
}
70+
71+
// [protocol[(address)]]
72+
if len(cfg.Net) > 0 {
73+
buf.WriteString(cfg.Net)
74+
if len(cfg.Addr) > 0 {
75+
buf.WriteByte('(')
76+
buf.WriteString(cfg.Addr)
77+
buf.WriteByte(')')
78+
}
79+
}
80+
81+
// /dbname
82+
buf.WriteByte('/')
83+
buf.WriteString(cfg.DBName)
84+
85+
// [?param1=value1&...&paramN=valueN]
86+
hasParam := false
87+
88+
if cfg.AllowAllFiles {
89+
hasParam = true
90+
buf.WriteString("?allowAllFiles=true")
91+
}
92+
93+
if cfg.AllowCleartextPasswords {
94+
if hasParam {
95+
buf.WriteString("&allowCleartextPasswords=true")
96+
} else {
97+
hasParam = true
98+
buf.WriteString("?allowCleartextPasswords=true")
99+
}
100+
}
101+
102+
if cfg.AllowOldPasswords {
103+
if hasParam {
104+
buf.WriteString("&allowOldPasswords=true")
105+
} else {
106+
hasParam = true
107+
buf.WriteString("?allowOldPasswords=true")
108+
}
109+
}
110+
111+
if cfg.ClientFoundRows {
112+
if hasParam {
113+
buf.WriteString("&clientFoundRows=true")
114+
} else {
115+
hasParam = true
116+
buf.WriteString("?clientFoundRows=true")
117+
}
118+
}
119+
120+
if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
121+
if hasParam {
122+
buf.WriteString("&collation=")
123+
} else {
124+
hasParam = true
125+
buf.WriteString("?collation=")
126+
}
127+
buf.WriteString(col)
128+
}
129+
130+
if cfg.ColumnsWithAlias {
131+
if hasParam {
132+
buf.WriteString("&columnsWithAlias=true")
133+
} else {
134+
hasParam = true
135+
buf.WriteString("?columnsWithAlias=true")
136+
}
137+
}
138+
139+
if cfg.InterpolateParams {
140+
if hasParam {
141+
buf.WriteString("&interpolateParams=true")
142+
} else {
143+
hasParam = true
144+
buf.WriteString("?interpolateParams=true")
145+
}
146+
}
147+
148+
if cfg.Loc != time.UTC && cfg.Loc != nil {
149+
if hasParam {
150+
buf.WriteString("&loc=")
151+
} else {
152+
hasParam = true
153+
buf.WriteString("?loc=")
154+
}
155+
buf.WriteString(url.QueryEscape(cfg.Loc.String()))
156+
}
157+
158+
if cfg.MultiStatements {
159+
if hasParam {
160+
buf.WriteString("&multiStatements=true")
161+
} else {
162+
hasParam = true
163+
buf.WriteString("?multiStatements=true")
164+
}
165+
}
166+
167+
if cfg.ParseTime {
168+
if hasParam {
169+
buf.WriteString("&parseTime=true")
170+
} else {
171+
hasParam = true
172+
buf.WriteString("?parseTime=true")
173+
}
174+
}
175+
176+
if cfg.ReadTimeout > 0 {
177+
if hasParam {
178+
buf.WriteString("&readTimeout=")
179+
} else {
180+
hasParam = true
181+
buf.WriteString("?readTimeout=")
182+
}
183+
buf.WriteString(cfg.ReadTimeout.String())
184+
}
185+
186+
if cfg.Strict {
187+
if hasParam {
188+
buf.WriteString("&strict=true")
189+
} else {
190+
hasParam = true
191+
buf.WriteString("?strict=true")
192+
}
193+
}
194+
195+
if cfg.Timeout > 0 {
196+
if hasParam {
197+
buf.WriteString("&timeout=")
198+
} else {
199+
hasParam = true
200+
buf.WriteString("?timeout=")
201+
}
202+
buf.WriteString(cfg.Timeout.String())
203+
}
204+
205+
if len(cfg.TLSConfig) > 0 {
206+
if hasParam {
207+
buf.WriteString("&tls=")
208+
} else {
209+
hasParam = true
210+
buf.WriteString("?tls=")
211+
}
212+
buf.WriteString(url.QueryEscape(cfg.TLSConfig))
213+
}
214+
215+
if cfg.WriteTimeout > 0 {
216+
if hasParam {
217+
buf.WriteString("&writeTimeout=")
218+
} else {
219+
hasParam = true
220+
buf.WriteString("?writeTimeout=")
221+
}
222+
buf.WriteString(cfg.WriteTimeout.String())
223+
}
224+
225+
// other params
226+
if cfg.Params != nil {
227+
for param, value := range cfg.Params {
228+
if hasParam {
229+
buf.WriteByte('&')
230+
} else {
231+
hasParam = true
232+
buf.WriteByte('?')
233+
}
234+
235+
buf.WriteString(param)
236+
buf.WriteByte('=')
237+
buf.WriteString(url.QueryEscape(value))
238+
}
239+
}
240+
241+
return buf.String()
242+
}
243+
54244
// ParseDSN parses the DSN string to a Config
55245
func ParseDSN(dsn string) (cfg *Config, err error) {
56246
// New config with some default values
@@ -196,15 +386,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
196386

197387
// Collation
198388
case "collation":
199-
collation, ok := collations[value]
200-
if !ok {
201-
// Note possibility for false negatives:
202-
// could be triggered although the collation is valid if the
203-
// collations map does not contain entries the server supports.
204-
err = errors.New("unknown collation")
205-
return
206-
}
207-
cfg.Collation = collation
389+
cfg.Collation = value
208390
break
209391

210392
case "columnsWithAlias":
@@ -279,24 +461,32 @@ func parseDSNParams(cfg *Config, params string) (err error) {
279461
boolValue, isBool := readBool(value)
280462
if isBool {
281463
if boolValue {
282-
cfg.TLS = &tls.Config{}
464+
cfg.TLSConfig = "true"
465+
cfg.tls = &tls.Config{}
466+
} else {
467+
cfg.TLSConfig = "false"
283468
}
284-
} else if value, err := url.QueryUnescape(value); err != nil {
285-
return fmt.Errorf("invalid value for TLS config name: %v", err)
469+
} else if vl := strings.ToLower(value); vl == "skip-verify" {
470+
cfg.TLSConfig = vl
471+
cfg.tls = &tls.Config{InsecureSkipVerify: true}
286472
} else {
287-
if strings.ToLower(value) == "skip-verify" {
288-
cfg.TLS = &tls.Config{InsecureSkipVerify: true}
289-
} else if tlsConfig, ok := tlsConfigRegister[value]; ok {
473+
name, err := url.QueryUnescape(value)
474+
if err != nil {
475+
return fmt.Errorf("invalid value for TLS config name: %v", err)
476+
}
477+
478+
if tlsConfig, ok := tlsConfigRegister[name]; ok {
290479
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
291480
host, _, err := net.SplitHostPort(cfg.Addr)
292481
if err == nil {
293482
tlsConfig.ServerName = host
294483
}
295484
}
296485

297-
cfg.TLS = tlsConfig
486+
cfg.TLSConfig = name
487+
cfg.tls = tlsConfig
298488
} else {
299-
return errors.New("invalid value / unknown config name: " + value)
489+
return errors.New("invalid value / unknown config name: " + name)
300490
}
301491
}
302492

‎dsn_test.go

+45-19
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ var testDSNs = []struct {
1919
in string
2020
out string
2121
}{
22-
{"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
23-
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
24-
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false}"},
25-
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
26-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
27-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
28-
{"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Loc:UTC TLS:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s Collation:224 AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
29-
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Loc:Local TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
30-
{"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
31-
{"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
32-
{"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
33-
{"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
34-
{"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
35-
{"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
22+
{"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
23+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
24+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false}"},
25+
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
26+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
27+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
28+
{"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
29+
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
30+
{"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
31+
{"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
32+
{"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
33+
{"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
34+
{"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
35+
{"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
3636
}
3737

3838
func TestDSNParser(t *testing.T) {
@@ -47,7 +47,7 @@ func TestDSNParser(t *testing.T) {
4747
}
4848

4949
// pointer not static
50-
cfg.TLS = nil
50+
cfg.tls = nil
5151

5252
res = fmt.Sprintf("%+v", cfg)
5353
if res != tst.out {
@@ -74,6 +74,32 @@ func TestDSNParserInvalid(t *testing.T) {
7474
}
7575
}
7676

77+
func TestDSNReformat(t *testing.T) {
78+
for i, tst := range testDSNs {
79+
dsn1 := tst.in
80+
cfg1, err := ParseDSN(dsn1)
81+
if err != nil {
82+
t.Error(err.Error())
83+
continue
84+
}
85+
cfg1.tls = nil // pointer not static
86+
res1 := fmt.Sprintf("%+v", cfg1)
87+
88+
dsn2 := cfg1.FormatDSN()
89+
cfg2, err := ParseDSN(dsn2)
90+
if err != nil {
91+
t.Error(err.Error())
92+
continue
93+
}
94+
cfg2.tls = nil // pointer not static
95+
res2 := fmt.Sprintf("%+v", cfg2)
96+
97+
if res1 != res2 {
98+
t.Errorf("%d. %q does not match %q", i, res2, res1)
99+
}
100+
}
101+
}
102+
77103
func TestDSNWithCustomTLS(t *testing.T) {
78104
baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
79105
tlsCfg := tls.Config{}
@@ -96,7 +122,7 @@ func TestDSNWithCustomTLS(t *testing.T) {
96122

97123
if err != nil {
98124
t.Error(err.Error())
99-
} else if cfg.TLS.ServerName != name {
125+
} else if cfg.tls.ServerName != name {
100126
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
101127
}
102128

@@ -107,14 +133,14 @@ func TestDSNWithCustomTLS(t *testing.T) {
107133

108134
if err != nil {
109135
t.Error(err.Error())
110-
} else if cfg.TLS.ServerName != name {
136+
} else if cfg.tls.ServerName != name {
111137
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
112138
}
113139

114140
DeregisterTLSConfig("utils_test")
115141
}
116142

117-
func TestDSNWithCustomTLS_queryEscape(t *testing.T) {
143+
func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
118144
const configKey = "&%!:"
119145
dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
120146
name := "foohost"
@@ -126,7 +152,7 @@ func TestDSNWithCustomTLS_queryEscape(t *testing.T) {
126152

127153
if err != nil {
128154
t.Error(err.Error())
129-
} else if cfg.TLS.ServerName != name {
155+
} else if cfg.tls.ServerName != name {
130156
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
131157
}
132158
}

‎packets.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"crypto/tls"
1414
"database/sql/driver"
1515
"encoding/binary"
16+
"errors"
1617
"fmt"
1718
"io"
1819
"math"
@@ -166,7 +167,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
166167
if mc.flags&clientProtocol41 == 0 {
167168
return nil, ErrOldProtocol
168169
}
169-
if mc.flags&clientSSL == 0 && mc.cfg.TLS != nil {
170+
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
170171
return nil, ErrNoTLS
171172
}
172173
pos += 2
@@ -232,7 +233,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
232233
}
233234

234235
// To enable TLS / SSL
235-
if mc.cfg.TLS != nil {
236+
if mc.cfg.tls != nil {
236237
clientFlags |= clientSSL
237238
}
238239

@@ -272,18 +273,25 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
272273
data[11] = 0x00
273274

274275
// Charset [1 byte]
275-
data[12] = mc.cfg.Collation
276+
var found bool
277+
data[12], found = collations[mc.cfg.Collation]
278+
if !found {
279+
// Note possibility for false negatives:
280+
// could be triggered although the collation is valid if the
281+
// collations map does not contain entries the server supports.
282+
return errors.New("unknown collation")
283+
}
276284

277285
// SSL Connection Request Packet
278286
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
279-
if mc.cfg.TLS != nil {
287+
if mc.cfg.tls != nil {
280288
// Send TLS / SSL request packet
281289
if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
282290
return err
283291
}
284292

285293
// Switch to TLS
286-
tlsConn := tls.Client(mc.netConn, mc.cfg.TLS)
294+
tlsConn := tls.Client(mc.netConn, mc.cfg.tls)
287295
if err := tlsConn.Handshake(); err != nil {
288296
return err
289297
}

0 commit comments

Comments
 (0)
Please sign in to comment.