diff --git a/README.cn.md b/README.cn.md index 8101eaafe1deac8b223a1349c365716d3dae6dec..073fe45c0adea34c3da9b07ff0351af14188aecf 100644 --- a/README.cn.md +++ b/README.cn.md @@ -41,6 +41,7 @@ pq: Invalid username/password,login denied. * sslkey * sslinline 指定sslkey/sslcert是字符串,而不是文件名 * sslpassword 指定sslkey密码短语 + * sslusetlcp 国密tlcp * 处理`database/sql`坏连接 * 正确扫描`time.Time`(即`timestamp[tz]`, `time[tz]`, `date`) * 正确扫描二进制Blob(即`bytea`) @@ -100,6 +101,63 @@ func main() { } ``` +## ssl + +数据库参数 + +``` +select name,setting from pg_settings where name like 'ssl%'; + name | setting +-------------------------+---------------------------------- + ssl | on + ssl_ca_file | cacert.pem + ssl_cert_file | server.crt + ssl_cert_notify_time | 90 + ssl_ciphers | ALL + ssl_crl_file | + ssl_key_file | server.key + ssl_renegotiation_limit | 0 +``` + +连接字符串 + +```go +dns := "host=172.23.1.51 port=26052 user=mtk password=mtkOP@123 dbname=postgres " + + "sslmode=verify-ca " + + "sslcert=client.crt sslkey=client.key sslrootcert=cacert.pem " + + "sslpassword=mtkOP@123" +``` + +## tlcp + +数据库参数 + +```sql +select name,setting from pg_settings where name like 'ssl%'; + name | setting +-------------------------+---------------------------------- + ssl | on + ssl_ca_file | /data/certs/CA.crt + ssl_cert_file | /data/certs/server.crt + ssl_cert_notify_time | 90 + ssl_ciphers | ALL + ssl_crl_file | + ssl_enc_cert_file | /data/certs/server_enc.crt + ssl_enc_key_file | /data/certs/server_enc.key + ssl_key_file | /data/certs/server.key + ssl_renegotiation_limit | 0 + ssl_use_tlcp | on +``` + +连接字符串 + +``` +dns := "host=172.23.1.51 port=27000 user=mtk password=mtkOP@123 dbname=postgres " + + "sslmode=verify-ca " + + "sslcert=client.crt sslkey=client.key sslrootcert=CA.crt " + + "sslusetlcp=on" +``` + ## 测试 `go test`适用于测试。 有关更多详细信息,请参见[测试.md](TESTS.md)。 diff --git a/array.go b/array.go index 39c8f7e2e0322ed7890302dbaa509bc0691103d2..9957c04891d26ba83859d7cc90103365efbfa631 100644 --- a/array.go +++ b/array.go @@ -19,10 +19,11 @@ var typeSQLScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem() // slice of any dimension. // // For example: -// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401})) // -// var x []sql.NullInt64 -// db.QueryRow(`SELECT ARRAY[235, 401]`).Scan(pq.Array(&x)) +// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401})) +// +// var x []sql.NullInt64 +// db.QueryRow(`SELECT ARRAY[235, 401]`).Scan(pq.Array(&x)) // // Scanning multi-dimensional arrays is not supported. Arrays where the lower // bound is not one (such as `[0:0]={1}') are not supported. diff --git a/config.go b/config.go index adfd13057503003c14b27101c50eaa4eee72c414..ae4be7e439276ccefde1d0fa5d6567320a417c8f 100644 --- a/config.go +++ b/config.go @@ -1,11 +1,11 @@ package pq import ( - "crypto/tls" "errors" "fmt" pgpassfile "gitee.com/opengauss/openGauss-connector-go-pq/pgpassfile" "gitee.com/opengauss/openGauss-connector-go-pq/pgservicefile" + "github.com/tjfoc/gmsm/gmtls" "math" "net" "net/url" @@ -36,6 +36,7 @@ const ( paramSSLRootCert = "sslrootcert" paramSSLinLine = "sslinline" paramSSLPassword = "sslpassword" + paramSSLTLcp = "sslusetlcp" paramService = "service" paramKrbSrvName = "krbsrvname" paramKrbSpn = "krbspn" @@ -52,7 +53,7 @@ type Config struct { Database string User string Password string - TLSConfig *tls.Config // nil disables TLS + TLSConfig *gmtls.Config // nil disables TLS ConnectTimeout time.Duration DialFunc DialFunc // e.g. net.Dialer.DialContext LookupFunc LookupFunc // e.g. net.Resolver.LookupHost @@ -136,7 +137,7 @@ func (c *Config) shouldLog(lvl LogLevel) bool { type FallbackConfig struct { Host string // host (e.g. localhost) or path to unix domain socket directory (e.g. /private/tmp) Port uint16 - TLSConfig *tls.Config // nil disables TLS + TLSConfig *gmtls.Config // nil disables TLS } // NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with @@ -158,11 +159,11 @@ func NetworkAddress(host string, port uint16) (network, address string) { // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be // empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file. // -// # Example DSN -// user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca +// # Example DSN +// user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca // -// # Example URL -// postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca +// # Example URL +// postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca // // The returned *Config may be modified. However, it is strongly recommended that any configuration that can be done // through the connection string be done there. In particular the fields Host, Port, TLSConfig, and Fallbacks can be @@ -173,27 +174,27 @@ func NetworkAddress(host string, port uint16) (network, address string) { // values that will be tried in order. This can be used as part of a high availability system. See // https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information. // -// # Example URL -// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb +// # Example URL +// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb // // ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed // via database URL or DSN: // -// PGHOST -// PGPORT -// PGDATABASE -// PGUSER -// PGPASSWORD -// PGPASSFILE -// PGSERVICE -// PGSERVICEFILE -// PGSSLMODE -// PGSSLCERT -// PGSSLKEY -// PGSSLROOTCERT -// PGAPPNAME -// PGCONNECT_TIMEOUT -// PGTARGETSESSIONATTRS +// PGHOST +// PGPORT +// PGDATABASE +// PGUSER +// PGPASSWORD +// PGPASSFILE +// PGSERVICE +// PGSERVICEFILE +// PGSSLMODE +// PGSSLCERT +// PGSSLKEY +// PGSSLROOTCERT +// PGAPPNAME +// PGCONNECT_TIMEOUT +// PGTARGETSESSIONATTRS // // See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables. // @@ -224,11 +225,11 @@ func NetworkAddress(host string, port uint16) (network, address string) { // // In addition, ParseConfig accepts the following options: // -// min_read_buffer_size -// The minimum size of the internal read buffer. Default 8192. -// servicefile -// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a -// part of the connection string. +// min_read_buffer_size +// The minimum size of the internal read buffer. Default 8192. +// servicefile +// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a +// part of the connection string. func ParseConfig(connString string) (*Config, error) { defaultSettings := defaultSettings() envSettings := parseEnvSettings() @@ -300,6 +301,7 @@ func ParseConfig(connString string) (*Config, error) { paramSSLRootCert: {}, paramSSLinLine: {}, paramSSLPassword: {}, + paramSSLTLcp: {}, paramTargetSessionAttrs: {}, paramMinReadBufferSize: {}, paramService: {}, @@ -354,7 +356,7 @@ func ParseConfig(connString string) (*Config, error) { return nil, &parseConfigError{connString: connString, msg: "invalid port", err: err} } - var tlsConfigs []*tls.Config + var tlsConfigs []*gmtls.Config // Ignore TLS settings if Unix domain socket like libpq if network, _ := NetworkAddress(host, port); network == "unix" { diff --git a/config_test.go b/config_test.go index 3d9f5ca32a6ba6abc6a1f031e676afcdc2c6eb33..cf3c66fe9e71d9b466988a84b7328f094db41537 100644 --- a/config_test.go +++ b/config_test.go @@ -1,10 +1,9 @@ package pq import ( - "crypto/tls" "fmt" + "github.com/tjfoc/gmsm/gmtls" "io/ioutil" - "log" "os" "os/user" "runtime" @@ -44,7 +43,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -66,7 +65,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -89,7 +88,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -130,7 +129,7 @@ func TestParseConfig(t *testing.T) { { Host: "localhost", Port: 5432, - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, }, @@ -147,7 +146,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -169,7 +168,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -184,7 +183,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -199,7 +198,7 @@ func TestParseConfig(t *testing.T) { Host: "localhost", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ServerName: "localhost"}, + TLSConfig: &gmtls.Config{ServerName: "localhost"}, RuntimeParams: map[string]string{}, }, }, @@ -530,7 +529,7 @@ func TestParseConfig(t *testing.T) { Host: "foo", Port: 5432, Database: "mydb", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -543,7 +542,7 @@ func TestParseConfig(t *testing.T) { { Host: "bar", Port: 5432, - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }}, { @@ -554,7 +553,7 @@ func TestParseConfig(t *testing.T) { { Host: "baz", Port: 5432, - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, }, @@ -744,7 +743,7 @@ func TestParseConfigEnvLibpq(t *testing.T) { User: osUserName, Host: "123.123.123.123", Port: 5432, - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -872,7 +871,7 @@ application_name = spaced string Database: "abcdb", User: "abcuser", Port: 9999, - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{}, @@ -893,7 +892,7 @@ application_name = spaced string Port: 5432, Database: "defdb", User: "defuser", - TLSConfig: &tls.Config{ + TLSConfig: &gmtls.Config{ InsecureSkipVerify: true, }, RuntimeParams: map[string]string{ @@ -945,19 +944,3 @@ func TestParseConfigExtractsMinReadBufferSize(t *testing.T) { // The buffer size is internal so there isn't much that can be done to test it other than see that the runtime param // was removed. } - -func TestParseConfigCustomTls(t *testing.T) { - tlsConfig := &tls.Config{ - ServerName: "pq.example.com", - } - err := RegisterTLSConfig("custom", tlsConfig) - if err != nil { - log.Fatal(err) - } - connStr := "host=pq.example.com port=5432 user=user1 dbname=pqgotest password=pqgotest sslmode=custom" - config, err := ParseConfig(connStr) - if err != nil { - t.Fatal() - } - assert.Equalf(t, tlsConfig, config.TLSConfig, "Custom Tls") -} diff --git a/conn.go b/conn.go index 54eff2cc384635d6100c3ad38b27b459e223dcad..ec55696d8dc755963cd2d12693eac34518c17593 100644 --- a/conn.go +++ b/conn.go @@ -4,11 +4,11 @@ import ( "bufio" "context" "crypto/md5" - "crypto/tls" "database/sql/driver" "encoding/binary" "errors" "fmt" + "github.com/tjfoc/gmsm/gmtls" "golang.org/x/text/encoding" "io" "net" @@ -212,7 +212,7 @@ func (cn *conn) DebugLog(msg string, data map[string]interface{}) { ) } -func (cn *conn) startTLS(tlsConfig *tls.Config) (err error) { +func (cn *conn) startTLS(tlsConfig *gmtls.Config) (err error) { err = binary.Write(cn.c, binary.BigEndian, []int32{8, 80877103}) if err != nil { return @@ -226,9 +226,7 @@ func (cn *conn) startTLS(tlsConfig *tls.Config) (err error) { if response[0] != 'S' { return ErrSSLNotSupported } - - cn.c = tls.Client(cn.c, tlsConfig) - + cn.c = gmtls.Client(cn.c, tlsConfig) return nil } @@ -1225,10 +1223,10 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) { // QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be // used as part of an SQL statement. For example: // -// tblname := "my_table" -// data := "my_data" -// quoted := pq.QuoteIdentifier(tblname) -// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) +// tblname := "my_table" +// data := "my_data" +// quoted := pq.QuoteIdentifier(tblname) +// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) // // Any double quotes in name will be escaped. The quoted identifier will be // case sensitive when used in a query. If the input string contains a zero @@ -1245,8 +1243,8 @@ func QuoteIdentifier(name string) string { // to DDL and other statements that do not accept parameters) to be used as part // of an SQL statement. For example: // -// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z") -// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date)) +// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z") +// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date)) // // Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be // replaced by two backslashes (i.e. "\\") and the C-style escape identifier diff --git a/conn_test.go b/conn_test.go index 61cd390731c176b3c05c42312d260726d00dd362..46d0a2cd45cfa8b8051f6588da14431f3a617a9c 100644 --- a/conn_test.go +++ b/conn_test.go @@ -1071,12 +1071,18 @@ func TestIssue282(t *testing.T) { } } -/* TestReadFloatPrecision +/* + TestReadFloatPrecision + omm=# SELECT float4 '0.10000122', float8 '35.03554004971999', float4 '1.2' omm-# ; - float4 | float8 | float4 + + float4 | float8 | float4 + ---------+----------------+-------- - .100001 | 35.03554004972 | 1.2 + + .100001 | 35.03554004972 | 1.2 + (1 row) */ func TestReadFloatPrecision(t *testing.T) { @@ -1715,124 +1721,123 @@ func TestCopyInStmtAffectedRows(t *testing.T) { // return dbCharset, err // } -// func TestGbkToUTF8Encoding(t *testing.T) { -// os.Setenv( -// "TEST_CONN_STRING", -// "postgres://hongye:hongye@50@121.36.15.2:55448/hongye_gbk?sslmode=disable"+ -// "&loggerLevel=debug", -// ) -// db := openTestConn(t) -// defer db.Close() -// var ( -// err error -// ) -// // dbCharset, err := getDBCharset(db) -// // if err != nil { -// // t.Errorf("getDBCharset %v", err) -// // } +// func TestGbkToUTF8Encoding(t *testing.T) { +// os.Setenv( +// "TEST_CONN_STRING", +// "postgres://hongye:hongye@50@121.36.15.2:55448/hongye_gbk?sslmode=disable"+ +// "&loggerLevel=debug", +// ) +// db := openTestConn(t) +// defer db.Close() +// var ( +// err error +// ) +// // dbCharset, err := getDBCharset(db) +// // if err != nil { +// // t.Errorf("getDBCharset %v", err) +// // } // -// // if !strings.EqualFold(dbCharset, "UTF8") { -// // t.Skip("Database Charset not utf8") -// // return -// // } -// _, _ = db.Exec("drop table test_utf8_gbk") -// _, err = db.Exec( -// "CREATE TABLE test_utf8_gbk (" + -// "id bigint," + -// "COL_UTF8 varchar(100)," + -// "COL_GBK varchar(100)" + -// ")", -// ) -// if err != nil { -// t.Fatal(err) -// } -// var clientCharset string -// _ = db.QueryRow("show client_encoding").Scan(&clientCharset) -// var ( -// id = 1 -// str = "请求IC卡系统响应" -// ) -// -// gbkEnc, err := ianaindex.MIB.Encoding("GBk") -// if err != nil { -// t.Errorf(`%+v`, err) -// return -// } -// tmp, err := ioutil.ReadAll( -// transform.NewReader(bytes.NewReader([]byte(str)), gbkEnc.NewEncoder()), -// ) -// if err != nil { -// t.Errorf(`%+v`, err) -// return -// } -// gbkStr := string(tmp) +// // if !strings.EqualFold(dbCharset, "UTF8") { +// // t.Skip("Database Charset not utf8") +// // return +// // } +// _, _ = db.Exec("drop table test_utf8_gbk") +// _, err = db.Exec( +// "CREATE TABLE test_utf8_gbk (" + +// "id bigint," + +// "COL_UTF8 varchar(100)," + +// "COL_GBK varchar(100)" + +// ")", +// ) +// if err != nil { +// t.Fatal(err) +// } +// var clientCharset string +// _ = db.QueryRow("show client_encoding").Scan(&clientCharset) +// var ( +// id = 1 +// str = "请求IC卡系统响应" +// ) // -// utf8Enc, err := ianaindex.MIB.Encoding("UTF-8") -// if err != nil { -// t.Errorf(`%+v`, err) -// return -// } -// tmp, err = ioutil.ReadAll( -// transform.NewReader(bytes.NewReader([]byte(gbkStr)), utf8Enc.NewEncoder()), -// ) -// if err != nil { -// t.Errorf(`%+v`, err) -// return -// } -// gbkUtf8Str := string(tmp) -// _, err = db.Exec( -// "insert into test_utf8_gbk (id,COL_UTF8,COL_GBK) values (:1,:2,:3)", id, str, gbkStr, -// ) -// if err != nil { -// t.Error(err) -// return -// } -// // _, err = stmt.Exec(id, str, gbkStr) -// // if err != nil { -// // t.Error(err) -// // return -// // } -// tx, err := db.Begin() -// stmt, err := tx.Prepare("copy test_utf8_gbk(id,COL_UTF8,COL_GBK) from stdin") -// if err != nil { -// t.Error(err) -// return -// } +// gbkEnc, err := ianaindex.MIB.Encoding("GBk") +// if err != nil { +// t.Errorf(`%+v`, err) +// return +// } +// tmp, err := ioutil.ReadAll( +// transform.NewReader(bytes.NewReader([]byte(str)), gbkEnc.NewEncoder()), +// ) +// if err != nil { +// t.Errorf(`%+v`, err) +// return +// } +// gbkStr := string(tmp) // -// if _, err = stmt.Exec(id+1, str, gbkStr); err != nil { -// t.Error(err) -// return -// } -// if _, err := stmt.Exec(); err != nil { -// t.Error(err) -// return -// } -// _ = tx.Commit() +// utf8Enc, err := ianaindex.MIB.Encoding("UTF-8") +// if err != nil { +// t.Errorf(`%+v`, err) +// return +// } +// tmp, err = ioutil.ReadAll( +// transform.NewReader(bytes.NewReader([]byte(gbkStr)), utf8Enc.NewEncoder()), +// ) +// if err != nil { +// t.Errorf(`%+v`, err) +// return +// } +// gbkUtf8Str := string(tmp) +// _, err = db.Exec( +// "insert into test_utf8_gbk (id,COL_UTF8,COL_GBK) values (:1,:2,:3)", id, str, gbkStr, +// ) +// if err != nil { +// t.Error(err) +// return +// } +// // _, err = stmt.Exec(id, str, gbkStr) +// // if err != nil { +// // t.Error(err) +// // return +// // } +// tx, err := db.Begin() +// stmt, err := tx.Prepare("copy test_utf8_gbk(id,COL_UTF8,COL_GBK) from stdin") +// if err != nil { +// t.Error(err) +// return +// } // -// os.Setenv( -// "TEST_CONN_STRING", -// "postgres://hongye:hongye@50@121.36.15.2:55448/hongye_gbk?sslmode=disable"+ -// "&loggerLevel=debug", -// ) -// db1 := openTestConn(t) -// defer db1.Close() -// _ = db1.QueryRow("show client_encoding").Scan(&clientCharset) -// rows, err := db1.Query("select id,COL_UTF8,COL_GBK from test_utf8_gbk") -// if err != nil { -// t.Error(err) -// return -// } -// for rows.Next() { -// var id, colUtf8, colGbk string -// if err := rows.Scan(&id, &colUtf8, &colGbk); err != nil { -// t.Error(err) -// return -// } -// fmt.Printf("%s %s(%x) %s(%x)\n", id, colUtf8, colUtf8, colGbk, colGbk) -// assert.Equal(t, gbkUtf8Str, colGbk) -// } -// } +// if _, err = stmt.Exec(id+1, str, gbkStr); err != nil { +// t.Error(err) +// return +// } +// if _, err := stmt.Exec(); err != nil { +// t.Error(err) +// return +// } +// _ = tx.Commit() // +// os.Setenv( +// "TEST_CONN_STRING", +// "postgres://hongye:hongye@50@121.36.15.2:55448/hongye_gbk?sslmode=disable"+ +// "&loggerLevel=debug", +// ) +// db1 := openTestConn(t) +// defer db1.Close() +// _ = db1.QueryRow("show client_encoding").Scan(&clientCharset) +// rows, err := db1.Query("select id,COL_UTF8,COL_GBK from test_utf8_gbk") +// if err != nil { +// t.Error(err) +// return +// } +// for rows.Next() { +// var id, colUtf8, colGbk string +// if err := rows.Scan(&id, &colUtf8, &colGbk); err != nil { +// t.Error(err) +// return +// } +// fmt.Printf("%s %s(%x) %s(%x)\n", id, colUtf8, colUtf8, colGbk, colGbk) +// assert.Equal(t, gbkUtf8Str, colGbk) +// } +// } func Test_Clob_Text_Blob_Bytea(t *testing.T) { db := openTestConn(t) defer db.Close() diff --git a/connector_example_test.go b/connector_example_test.go index 1427efdedfa7f68b96310a240250f994d117d014..7c6d8e3018c794ff542dcb3abc92cdd070af71cd 100644 --- a/connector_example_test.go +++ b/connector_example_test.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package pq_test diff --git a/connector_test.go b/connector_test.go index 4595dd4eece54928091518435721b59195e3bcdb..044c8c880637e5fe197bc6c133476cccbd68cbba 100644 --- a/connector_test.go +++ b/connector_test.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package pq diff --git a/defaults.go b/defaults.go index e853ad660ed004080ab63945f6f9aefeef233fe3..a9bdebe4cf78b143a8fdb0db18394d8a8a79be72 100644 --- a/defaults.go +++ b/defaults.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package pq diff --git a/doc.go b/doc.go index 3b8c01ac399f29ffc08cdc133966d1554d62a770..bf5425cbc80617a9636ea4a1f219915469f02364 100644 --- a/doc.go +++ b/doc.go @@ -27,9 +27,7 @@ You can also connect to a database using a URL. For example: connStr := "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" db, err := sql.Open("postgres", connStr) - -Connection String Parameters - +# Connection String Parameters Similarly to libpq, when establishing a connection using pq you are expected to supply a connection string containing zero or more parameters. @@ -42,42 +40,42 @@ them in the options parameter. For compatibility with libpq, the following special connection parameters are supported: - * dbname - The name of the database to connect to - * user - The user to sign in as - * password - The user's password - * host - The host to connect to. Values that start with / are for unix - domain sockets. (default is localhost) - * port - The port to bind to. (default is 5432) - * sslmode - Whether or not to use SSL (default is require, this is not - the default for libpq) - * fallback_application_name - An application_name to fall back to if one isn't provided. - * connect_timeout - Maximum wait for connection, in seconds. Zero or - not specified means wait indefinitely. - * sslcert - Cert file location. The file must contain PEM encoded data. - * sslkey - Key file location. The file must contain PEM encoded data. - * sslrootcert - The location of the root certificate file. The file - must contain PEM encoded data. + - dbname - The name of the database to connect to + - user - The user to sign in as + - password - The user's password + - host - The host to connect to. Values that start with / are for unix + domain sockets. (default is localhost) + - port - The port to bind to. (default is 5432) + - sslmode - Whether or not to use SSL (default is require, this is not + the default for libpq) + - fallback_application_name - An application_name to fall back to if one isn't provided. + - connect_timeout - Maximum wait for connection, in seconds. Zero or + not specified means wait indefinitely. + - sslcert - Cert file location. The file must contain PEM encoded data. + - sslkey - Key file location. The file must contain PEM encoded data. + - sslrootcert - The location of the root certificate file. The file + must contain PEM encoded data. Valid values for sslmode are: - * disable - No SSL - * require - Always SSL (skip verification) - * verify-ca - Always SSL (verify that the certificate presented by the - server was signed by a trusted CA) - * verify-full - Always SSL (verify that the certification presented by - the server was signed by a trusted CA and the server host name - matches the one in the certificate) + - disable - No SSL + - require - Always SSL (skip verification) + - verify-ca - Always SSL (verify that the certificate presented by the + server was signed by a trusted CA) + - verify-full - Always SSL (verify that the certification presented by + the server was signed by a trusted CA and the server host name + matches the one in the certificate) See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING for more information about connection string parameters. Use single quotes for values that contain whitespace: - "user=pqgotest password='with spaces'" + "user=pqgotest password='with spaces'" A backslash will escape the next character in values: - "user=space\ man password='it\'s valid'" + "user=space\ man password='it\'s valid'" Note that the connection parameter client_encoding (which sets the text encoding for the connection) may be set but must be "UTF8", @@ -98,9 +96,7 @@ provided connection parameters. The pgpass mechanism as described in http://www.postgresql.org/docs/current/static/libpq-pgpass.html is supported, but on Windows PGPASSFILE must be specified explicitly. - -Queries - +# Queries database/sql does not dictate any specific format for parameter markers in query strings, and pq uses the Postgres-native ordinal markers, @@ -125,9 +121,7 @@ For more details on RETURNING, see the Postgres documentation: For additional instructions on querying see the documentation for the database/sql package. - -Data Types - +# Data Types Parameters pass through driver.DefaultParameterConverter before they are handled by this package. When the binary_parameters connection option is enabled, @@ -135,30 +129,27 @@ by this package. When the binary_parameters connection option is enabled, This package returns the following types for values from the PostgreSQL backend: - - integer types smallint, integer, and bigint are returned as int64 - - floating-point types real and double precision are returned as float64 - - character types char, varchar, and text are returned as string - - temporal types date, time, timetz, timestamp, and timestamptz are - returned as time.Time - - the boolean type is returned as bool - - the bytea type is returned as []byte + - integer types smallint, integer, and bigint are returned as int64 + - floating-point types real and double precision are returned as float64 + - character types char, varchar, and text are returned as string + - temporal types date, time, timetz, timestamp, and timestamptz are + returned as time.Time + - the boolean type is returned as bool + - the bytea type is returned as []byte All other types are returned directly from the backend as []byte values in text format. - -Errors - +# Errors pq may return errors of type *pq.Error which can be interrogated for error details: - if err, ok := err.(*pq.Error); ok { - fmt.Println("pq error:", err.Code.Name()) - } + if err, ok := err.(*pq.Error); ok { + fmt.Println("pq error:", err.Code.Name()) + } See the pq.Error type for details. - -Bulk imports +# Bulk imports You can perform bulk imports by preparing a statement returned by pq.CopyIn (or pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement @@ -206,9 +197,7 @@ Usage example: log.Fatal(err) } - -Notifications - +# Notifications PostgreSQL supports a simple publish/subscribe model over database connections. See http://www.postgresql.org/docs/current/static/sql-notify.html @@ -241,9 +230,7 @@ bytes by the PostgreSQL server. You can find a complete, working example of Listener usage at https://godoc.org/gitee.com/opengauss/openGauss-connector-go-pq/example/listen. - -Kerberos Support - +# Kerberos Support If you need support for Kerberos authentication, add the following to your main package: @@ -259,10 +246,10 @@ don't have to download unnecessary dependencies. When imported, additional connection string parameters are supported: - * krbsrvname - GSS (Kerberos) service name when constructing the - SPN (default is `postgres`). This will be combined with the host - to form the full SPN: `krbsrvname/host`. - * krbspn - GSS (Kerberos) SPN. This takes priority over - `krbsrvname` if present. + - krbsrvname - GSS (Kerberos) service name when constructing the + SPN (default is `postgres`). This will be combined with the host + to form the full SPN: `krbsrvname/host`. + - krbspn - GSS (Kerberos) SPN. This takes priority over + `krbsrvname` if present. */ package pq diff --git a/example/listen/doc.go b/example/listen/doc.go index e5a7d73637cf4aaa6ba6fd722bf8f4daef82c8f4..59d534a5e63c30502f8070dec059b00509ab129d 100644 --- a/example/listen/doc.go +++ b/example/listen/doc.go @@ -1,98 +1,95 @@ /* - Package listen is a self-contained Go program which uses the LISTEN / NOTIFY mechanism to avoid polling the database while waiting for more work to arrive. - // - // You can see the program in action by defining a function similar to - // the following: - // - // CREATE OR REPLACE FUNCTION public.get_work() - // RETURNS bigint - // LANGUAGE sql - // AS $$ - // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END - // $$ - // ; - - package main - - import ( - "database/sql" - "fmt" - "time" - - "gitee.com/opengauss/openGauss-connector-go-pq" - ) - - func doWork(db *sql.DB, work int64) { - // work here - } - - func getWork(db *sql.DB) { - for { - // get work from the database here - var work sql.NullInt64 - err := db.QueryRow("SELECT get_work()").Scan(&work) - if err != nil { - fmt.Println("call to get_work() failed: ", err) - time.Sleep(10 * time.Second) - continue - } - if !work.Valid { - // no more work to do - fmt.Println("ran out of work") - return - } - - fmt.Println("starting work on ", work.Int64) - go doWork(db, work.Int64) - } - } - - func waitForNotification(l *pq.Listener) { - select { - case <-l.Notify: - fmt.Println("received notification, new work available") - case <-time.After(90 * time.Second): - go l.Ping() - // Check if there's more work available, just in case it takes - // a while for the Listener to notice connection loss and - // reconnect. - fmt.Println("received no work for 90 seconds, checking for new work") - } - } - - func main() { - var conninfo string = "" - - db, err := sql.Open("postgres", conninfo) - if err != nil { - panic(err) - } - - reportProblem := func(ev pq.ListenerEventType, err error) { - if err != nil { - fmt.Println(err.Error()) - } - } - - minReconn := 10 * time.Second - maxReconn := time.Minute - listener := pq.NewListener(conninfo, minReconn, maxReconn, reportProblem) - err = listener.Listen("getwork") - if err != nil { - panic(err) - } - - fmt.Println("entering main loop") - for { - // process all available work before waiting for notifications - getWork(db) - waitForNotification(listener) - } - } - - + // + // You can see the program in action by defining a function similar to + // the following: + // + // CREATE OR REPLACE FUNCTION public.get_work() + // RETURNS bigint + // LANGUAGE sql + // AS $$ + // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END + // $$ + // ; + + package main + + import ( + "database/sql" + "fmt" + "time" + + "gitee.com/opengauss/openGauss-connector-go-pq" + ) + + func doWork(db *sql.DB, work int64) { + // work here + } + + func getWork(db *sql.DB) { + for { + // get work from the database here + var work sql.NullInt64 + err := db.QueryRow("SELECT get_work()").Scan(&work) + if err != nil { + fmt.Println("call to get_work() failed: ", err) + time.Sleep(10 * time.Second) + continue + } + if !work.Valid { + // no more work to do + fmt.Println("ran out of work") + return + } + + fmt.Println("starting work on ", work.Int64) + go doWork(db, work.Int64) + } + } + + func waitForNotification(l *pq.Listener) { + select { + case <-l.Notify: + fmt.Println("received notification, new work available") + case <-time.After(90 * time.Second): + go l.Ping() + // Check if there's more work available, just in case it takes + // a while for the Listener to notice connection loss and + // reconnect. + fmt.Println("received no work for 90 seconds, checking for new work") + } + } + + func main() { + var conninfo string = "" + + db, err := sql.Open("postgres", conninfo) + if err != nil { + panic(err) + } + + reportProblem := func(ev pq.ListenerEventType, err error) { + if err != nil { + fmt.Println(err.Error()) + } + } + + minReconn := 10 * time.Second + maxReconn := time.Minute + listener := pq.NewListener(conninfo, minReconn, maxReconn, reportProblem) + err = listener.Listen("getwork") + if err != nil { + panic(err) + } + + fmt.Println("entering main loop") + for { + // process all available work before waiting for notifications + getWork(db) + waitForNotification(listener) + } + } */ package listen diff --git a/go.mod b/go.mod index a9bf6259112cf1504aae34aeddb3aca35068d725..a8fd22f88d970d9d2b68dce67859e3f6b95b5067 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,12 @@ module gitee.com/opengauss/openGauss-connector-go-pq go 1.13 require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/stretchr/testify v1.7.0 + github.com/kr/pretty v0.3.1 // indirect + github.com/stretchr/testify v1.8.4 github.com/tjfoc/gmsm v1.4.1 - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 - golang.org/x/text v0.3.3 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + golang.org/x/crypto v0.14.0 + golang.org/x/text v0.13.0 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 27bfe0749e5f8c216f11ab96954bf607e7264739..512ff6e6a111bc9e945dedbf330620547c87275c 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,57 +25,94 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -90,9 +128,10 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/logger.go b/logger.go index 1112ece39eb12fe925b3e9c9f26c4c7f75b0f79e..6fa3d16d88517386dbc52c5ecc4f5ca52c49cc9f 100644 --- a/logger.go +++ b/logger.go @@ -53,6 +53,7 @@ type Logger interface { // LogLevelFromString converts log level string to constant // // Valid levels: +// // trace // debug // info diff --git a/notice.go b/notice.go index 01dd8c723ddb772bb04bfff58c7f59293a482c21..70ad122a7d6b80a372646e98996525bb4686e482 100644 --- a/notice.go +++ b/notice.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package pq diff --git a/notice_example_test.go b/notice_example_test.go index e32d99bf90f6e80971ac64ac5685a37253c86d6e..96c3e80cce95170c0a3a5994dd4a32f1dd268ada 100644 --- a/notice_example_test.go +++ b/notice_example_test.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package pq_test diff --git a/notice_test.go b/notice_test.go index 0113f7eb3695526e4b033dedd22c005c46aaac56..e95466fd6d37ff4c66f04381af75a73554bb8957 100644 --- a/notice_test.go +++ b/notice_test.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package pq diff --git a/notify.go b/notify.go index 5c421fdb8b565b5ca3b62f19369a42cc648b98ee..5e68d5364936a55cd7c26adbc910b8a82846579b 100644 --- a/notify.go +++ b/notify.go @@ -330,11 +330,11 @@ func (l *ListenerConn) sendSimpleQuery(q string) (err error) { // ExecSimpleQuery executes a "simple query" (i.e. one with no bindable // parameters) on the connection. The possible return values are: -// 1) "executed" is true; the query was executed to completion on the -// database server. If the query failed, err will be set to the error -// returned by the database, otherwise err will be nil. -// 2) If "executed" is false, the query could not be executed on the remote -// server. err will be non-nil. +// 1. "executed" is true; the query was executed to completion on the +// database server. If the query failed, err will be set to the error +// returned by the database, otherwise err will be nil. +// 2. If "executed" is false, the query could not be executed on the remote +// server. err will be non-nil. // // After a call to ExecSimpleQuery has returned an executed=false value, the // connection has either been closed or will be closed shortly thereafter, and @@ -541,12 +541,12 @@ func (l *Listener) NotificationChannel() <-chan *Notification { // connection can not be re-established. // // Listen will only fail in three conditions: -// 1) The channel is already open. The returned error will be -// ErrChannelAlreadyOpen. -// 2) The query was executed on the remote server, but PostgreSQL returned an -// error message in response to the query. The returned error will be a -// pq.Error containing the information the server supplied. -// 3) Close is called on the Listener before the request could be completed. +// 1. The channel is already open. The returned error will be +// ErrChannelAlreadyOpen. +// 2. The query was executed on the remote server, but PostgreSQL returned an +// error message in response to the query. The returned error will be a +// pq.Error containing the information the server supplied. +// 3. Close is called on the Listener before the request could be completed. // // The channel name is case-sensitive. func (l *Listener) Listen(channel string) error { diff --git a/oid/gen.go b/oid/gen.go index 7bcb7738fc2d26081651d5a9a355538ab84f63b4..9de3bf8dbd255b75caf3b579980e6ae9b9dab69c 100644 --- a/oid/gen.go +++ b/oid/gen.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore // Generate the table of OID values diff --git a/rfcdigest.go b/rfcdigest.go index f339ec6af903704c6dca331fc8a661e7abf11def..576ba3b588e8a7ffdf22fb72dcc22169e30d34df 100644 --- a/rfcdigest.go +++ b/rfcdigest.go @@ -105,32 +105,33 @@ func bytesToHex(bytes []byte) []byte { /* RFC5802Algorithm - public static byte[] RFC5802Algorithm( - String password, String random64code, String token, String server_signature, int server_iteration) { - byte[] h = null; - byte[] result = null; - try { - byte[] K = generateKFromPBKDF2(password, random64code, server_iteration); - byte[] server_key = getKeyFromHmac(K, "Sever Key".getBytes("UTF-8")); - byte[] client_key = getKeyFromHmac(K, "Client Key".getBytes("UTF-8")); - byte[] stored_key = null; - if (getIsSha256()) { - stored_key = sha256(client_key); - } else { - stored_key = sm3(client_key); - } - byte[] tokenbyte = hexStringToBytes(token); - byte[] client_signature = getKeyFromHmac(server_key, tokenbyte); - if (server_signature != null && !server_signature.equals(bytesToHexString(client_signature))) return new byte[0]; - byte[] hmac_result = getKeyFromHmac(stored_key, tokenbyte); - h = XOR_between_password(hmac_result, client_key, client_key.length); - result = new byte[h.length * 2]; - bytesToHex(h, result, 0, h.length); - } catch (Exception e) { - LOGGER.info("RFC5802Algorithm failed. " + e.toString()); - } - return result; - } + + public static byte[] RFC5802Algorithm( + String password, String random64code, String token, String server_signature, int server_iteration) { + byte[] h = null; + byte[] result = null; + try { + byte[] K = generateKFromPBKDF2(password, random64code, server_iteration); + byte[] server_key = getKeyFromHmac(K, "Sever Key".getBytes("UTF-8")); + byte[] client_key = getKeyFromHmac(K, "Client Key".getBytes("UTF-8")); + byte[] stored_key = null; + if (getIsSha256()) { + stored_key = sha256(client_key); + } else { + stored_key = sm3(client_key); + } + byte[] tokenbyte = hexStringToBytes(token); + byte[] client_signature = getKeyFromHmac(server_key, tokenbyte); + if (server_signature != null && !server_signature.equals(bytesToHexString(client_signature))) return new byte[0]; + byte[] hmac_result = getKeyFromHmac(stored_key, tokenbyte); + h = XOR_between_password(hmac_result, client_key, client_key.length); + result = new byte[h.length * 2]; + bytesToHex(h, result, 0, h.length); + } catch (Exception e) { + LOGGER.info("RFC5802Algorithm failed. " + e.toString()); + } + return result; + } */ func RFC5802Algorithm(password string, random64code string, token string, serverSignature string, serverIteration int, method string) []byte { k := generateKFromPBKDF2(password, random64code, serverIteration) @@ -156,36 +157,36 @@ func RFC5802Algorithm(password string, random64code string, token string, server } /* - Md5Sha256encode - public static byte[] Md5Sha256encode(String password, String random64code, byte salt[]) { - MessageDigest md; - byte[] temp_digest, pass_digest; - byte[] hex_digest = new byte[35]; - try { - StringBuilder stringBuilder = new StringBuilder(""); - byte[] K = MD5Digest.generateKFromPBKDF2(password, random64code); - byte[] server_key = MD5Digest.getKeyFromHmac(K, "Sever Key".getBytes("UTF-8")); - byte[] client_key = MD5Digest.getKeyFromHmac(K, "Client Key".getBytes("UTF-8")); - byte[] stored_key = MD5Digest.sha256(client_key); - stringBuilder.append(random64code); - stringBuilder.append(MD5Digest.bytesToHexString(server_key)); - stringBuilder.append(MD5Digest.bytesToHexString(stored_key)); - String EncryptString = stringBuilder.toString(); - md = MessageDigest.getInstance("MD5"); - md.update(EncryptString.getBytes("UTF-8")); - md.update(salt); - pass_digest = md.digest(); - bytesToHex(pass_digest, hex_digest, 3, 16); - hex_digest[0] = (byte) 'm'; - hex_digest[1] = (byte) 'd'; - hex_digest[2] = (byte) '5'; - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - LOGGER.info("MD5_SHA256encode failed. ", e); - } catch (Exception e) { - LOGGER.info("MD5_SHA256encode failed. ", e); - } - return hex_digest; - } + Md5Sha256encode + public static byte[] Md5Sha256encode(String password, String random64code, byte salt[]) { + MessageDigest md; + byte[] temp_digest, pass_digest; + byte[] hex_digest = new byte[35]; + try { + StringBuilder stringBuilder = new StringBuilder(""); + byte[] K = MD5Digest.generateKFromPBKDF2(password, random64code); + byte[] server_key = MD5Digest.getKeyFromHmac(K, "Sever Key".getBytes("UTF-8")); + byte[] client_key = MD5Digest.getKeyFromHmac(K, "Client Key".getBytes("UTF-8")); + byte[] stored_key = MD5Digest.sha256(client_key); + stringBuilder.append(random64code); + stringBuilder.append(MD5Digest.bytesToHexString(server_key)); + stringBuilder.append(MD5Digest.bytesToHexString(stored_key)); + String EncryptString = stringBuilder.toString(); + md = MessageDigest.getInstance("MD5"); + md.update(EncryptString.getBytes("UTF-8")); + md.update(salt); + pass_digest = md.digest(); + bytesToHex(pass_digest, hex_digest, 3, 16); + hex_digest[0] = (byte) 'm'; + hex_digest[1] = (byte) 'd'; + hex_digest[2] = (byte) '5'; + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + LOGGER.info("MD5_SHA256encode failed. ", e); + } catch (Exception e) { + LOGGER.info("MD5_SHA256encode failed. ", e); + } + return hex_digest; + } */ func Md5Sha256encode(password, random64code string, salt []byte) []byte { k := generateKFromPBKDF2NoSerIter(password, random64code) diff --git a/scram/scram.go b/scram/scram.go index 477216b6008a59d369f9475105489c3ce5c37ac1..0e1ef65632837fe834426832533fc1bfcd6ab655 100644 --- a/scram/scram.go +++ b/scram/scram.go @@ -25,7 +25,6 @@ // Package scram implements a SCRAM-{SHA-1,etc} client per RFC5802. // // http://tools.ietf.org/html/rfc5802 -// package scram import ( @@ -43,17 +42,16 @@ import ( // // A Client may be used within a SASL conversation with logic resembling: // -// var in []byte -// var client = scram.NewClient(sha1.New, user, pass) -// for client.Step(in) { -// out := client.Out() -// // send out to server -// in := serverOut -// } -// if client.Err() != nil { -// // auth failed -// } -// +// var in []byte +// var client = scram.NewClient(sha1.New, user, pass) +// for client.Step(in) { +// out := client.Out() +// // send out to server +// in := serverOut +// } +// if client.Err() != nil { +// // auth failed +// } type Client struct { newHash func() hash.Hash @@ -73,8 +71,7 @@ type Client struct { // // For SCRAM-SHA-256, for example, use: // -// client := scram.NewClient(sha256.New, user, pass) -// +// client := scram.NewClient(sha256.New, user, pass) func NewClient(newHash func() hash.Hash, user, pass string) *Client { c := &Client{ newHash: newHash, diff --git a/ssl.go b/ssl.go index bc3ecc55f20a6375e8937d87d56b073e2065f345..b46216adba43764757a607517c990b348ada82c4 100644 --- a/ssl.go +++ b/ssl.go @@ -1,23 +1,27 @@ package pq import ( - "crypto/tls" + "crypto" + "crypto/ecdsa" + "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "fmt" + "github.com/tjfoc/gmsm/gmtls" + "github.com/tjfoc/gmsm/sm2" + gmx509 "github.com/tjfoc/gmsm/x509" "io/ioutil" - "net" "os" "os/user" "path" "strings" - "sync" ) // configTLS uses libpq's TLS parameters to construct []*tls.Config. It is // necessary to allow returning multiple TLS configs as sslmode "allow" and // "prefer" allow fallback. -func configTLS(settings map[string]string) ([]*tls.Config, error) { +func configTLS(settings map[string]string) ([]*gmtls.Config, error) { host := settings[paramHost] sslmode := settings[paramSSLMode] sslrootcert := settings[paramSSLRootCert] @@ -28,11 +32,11 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { if sslmode == "" { sslmode = "prefer" } - tlsConfig := &tls.Config{} + tlsConfig := &gmtls.Config{} switch sslmode { case "disable": - return []*tls.Config{nil}, nil + return []*gmtls.Config{nil}, nil case "allow", "prefer": tlsConfig.InsecureSkipVerify = true case "require": @@ -58,10 +62,10 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { // and https://pkg.go.dev/crypto/tls?tab=doc#example-Config-VerifyPeerCertificate // for more info. tlsConfig.InsecureSkipVerify = true - tlsConfig.VerifyPeerCertificate = func(certificates [][]byte, _ [][]*x509.Certificate) error { - certs := make([]*x509.Certificate, len(certificates)) + tlsConfig.VerifyPeerCertificate = func(certificates [][]byte, _ [][]*gmx509.Certificate) error { + certs := make([]*gmx509.Certificate, len(certificates)) for i, asn1Data := range certificates { - cert, err := x509.ParseCertificate(asn1Data) + cert, err := gmx509.ParseCertificate(asn1Data) if err != nil { return fmterrorf("failed to parse certificate from server: " + err.Error()) } @@ -69,9 +73,9 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { } // Leave DNSName empty to skip hostname verification. - opts := x509.VerifyOptions{ + opts := gmx509.VerifyOptions{ Roots: tlsConfig.RootCAs, - Intermediates: x509.NewCertPool(), + Intermediates: gmx509.NewCertPool(), } // Skip the first cert because it's the leaf. All others // are intermediates. @@ -84,26 +88,19 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { case "verify-full": tlsConfig.ServerName = host default: - tlsConf := getTLSConfigClone(sslmode) - if tlsConf == nil { - return nil, fmterrorf("sslmode is invalid") - } - return []*tls.Config{tlsConf}, nil + return nil, errors.New("sslmode is invalid") } if sslrootcert != "" { - caCertPool := x509.NewCertPool() - + caCertPool := gmx509.NewCertPool() caPath := sslrootcert caCert, err := ioutil.ReadFile(caPath) if err != nil { return nil, fmt.Errorf("unable to read CA file: %w", err) } - if !caCertPool.AppendCertsFromPEM(caCert) { return nil, fmterrorf("unable to add CA to cert pool") } - tlsConfig.RootCAs = caCertPool tlsConfig.ClientCAs = caCertPool } @@ -111,7 +108,6 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { if (sslcert != "" && sslkey == "") || (sslcert == "" && sslkey != "") { return nil, fmterrorf(`both "sslcert" and "sslkey" are required`) } - err := sslClientCertificates(tlsConfig, settings) if err != nil { return nil, err @@ -120,151 +116,27 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { if err != nil { return nil, err } - + // 国密 + if _, ok := settings[paramSSLTLcp]; ok { + tlsConfig.GMSupport = gmtls.NewGMSupport() + } switch sslmode { case "allow": - return []*tls.Config{nil, tlsConfig}, nil + return []*gmtls.Config{nil, tlsConfig}, nil case "prefer": - return []*tls.Config{tlsConfig, nil}, nil + return []*gmtls.Config{tlsConfig, nil}, nil case "require", "verify-ca", "verify-full": - return []*tls.Config{tlsConfig}, nil + return []*gmtls.Config{tlsConfig}, nil default: panic("BUG: bad sslmode should already have been caught") } } -var ( - tlsConfigLock sync.RWMutex - tlsConfigRegistry map[string]*tls.Config -) - -func RegisterTLSConfig(key string, config *tls.Config) error { - if _, isBool := readBool(key); isBool || - strings.ToLower(key) == "require" || - strings.ToLower(key) == "verify-ca" || - strings.ToLower(key) == "verify-full" || - strings.ToLower(key) == "disable" { - return fmt.Errorf("key '%s' is reserved", key) - } - - tlsConfigLock.Lock() - if tlsConfigRegistry == nil { - tlsConfigRegistry = make(map[string]*tls.Config) - } - - tlsConfigRegistry[key] = config - tlsConfigLock.Unlock() - return nil -} - -// DeregisterTLSConfig removes the tls.Config associated with key. -func DeregisterTLSConfig(key string) { - tlsConfigLock.Lock() - if tlsConfigRegistry != nil { - delete(tlsConfigRegistry, key) - } - tlsConfigLock.Unlock() -} - -func getTLSConfigClone(key string) (config *tls.Config) { - tlsConfigLock.RLock() - if v, ok := tlsConfigRegistry[key]; ok { - config = v.Clone() - } - tlsConfigLock.RUnlock() - return -} - -// Returns the bool value of the input. -// The 2nd return value indicates if the input was a valid bool value -func readBool(input string) (value bool, valid bool) { - switch input { - case "1", "true", "TRUE", "True": - return true, true - case "0", "false", "FALSE", "False": - return false, true - } - - // Not a valid bool value - return -} - -// ssl generates a function to upgrade a net.Conn based on the "sslmode" and -// related settings. The function is nil when no upgrade should take place. -func ssl(o values) (func(net.Conn) (net.Conn, error), error) { - verifyCaOnly := false - tlsConf := tls.Config{} - switch mode := o[paramSSLMode]; mode { - // "require" is the default. - case "", "require": - // We must skip TLS's own verification since it requires full - // verification since Go 1.3. - tlsConf.InsecureSkipVerify = true - - // From http://www.postgresql.org/docs/current/static/libpq-ssl.html: - // - // Note: For backwards compatibility with earlier versions of - // PostgreSQL, if a root CA file exists, the behavior of - // sslmode=require will be the same as that of verify-ca, meaning the - // server certificate is validated against the CA. Relying on this - // behavior is discouraged, and applications that need certificate - // validation should always use verify-ca or verify-full. - if sslrootcert, ok := o[paramSSLRootCert]; ok { - if _, err := os.Stat(sslrootcert); err == nil { - verifyCaOnly = true - } else { - delete(o, paramSSLRootCert) - } - } - case "verify-ca": - // We must skip TLS's own verification since it requires full - // verification since Go 1.3. - tlsConf.InsecureSkipVerify = true - verifyCaOnly = true - case "verify-full": - tlsConf.ServerName = o[paramHost] - case "disable": - return nil, nil - default: - return nil, fmterrorf( - `unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, - mode, - ) - } - - err := sslClientCertificates(&tlsConf, o) - if err != nil { - return nil, err - } - err = sslCertificateAuthority(&tlsConf, o) - if err != nil { - return nil, err - } - - // Accept renegotiation requests initiated by the backend. - // - // Renegotiation was deprecated then removed from PostgreSQL 9.5, but - // the default configuration of older versions has it enabled. Redshift - // also initiates renegotiations and cannot be reconfigured. - tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient - - return func(conn net.Conn) (net.Conn, error) { - client := tls.Client(conn, &tlsConf) - if verifyCaOnly { - err := sslVerifyCertificateAuthority(client, &tlsConf) - if err != nil { - return nil, err - } - } - return client, nil - }, nil -} - // sslClientCertificates adds the certificate specified in the "sslcert" and // "sslkey" settings, or if they aren't set, from the .postgresql directory // in the user's home directory. The configured files must exist and have // the correct permissions. -func sslClientCertificates(tlsConf *tls.Config, o values) error { +func sslClientCertificates(tlsConf *gmtls.Config, o values) error { var ( sslCertBytes []byte sslKeyBytes []byte @@ -341,23 +213,21 @@ func sslClientCertificates(tlsConf *tls.Config, o values) error { block.Headers = nil sslKeyBytes = pem.EncodeToMemory(block) } - cert, err := tls.X509KeyPair(sslCertBytes, sslKeyBytes) + cert, err := X509KeyPair(sslCertBytes, sslKeyBytes) if err != nil { - fmt.Println("2", err) return err } - - tlsConf.Certificates = []tls.Certificate{cert} + tlsConf.Certificates = []gmtls.Certificate{cert} return nil } // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. -func sslCertificateAuthority(tlsConf *tls.Config, o values) error { +func sslCertificateAuthority(tlsConf *gmtls.Config, o values) error { // In libpq, the root certificate is only loaded if the setting is not blank. // // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951 if sslrootcert := o[paramSSLRootCert]; len(sslrootcert) > 0 { - tlsConf.RootCAs = x509.NewCertPool() + tlsConf.RootCAs = gmx509.NewCertPool() sslinline := o[paramSSLinLine] @@ -380,26 +250,121 @@ func sslCertificateAuthority(tlsConf *tls.Config, o values) error { return nil } -// sslVerifyCertificateAuthority carries out a TLS handshake to the server and -// verifies the presented certificate against the CA, i.e. the one specified in -// sslrootcert or the system CA if sslrootcert was not specified. -func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error { - err := client.Handshake() +// X509KeyPair parses a public/private key pair from a pair of +// PEM encoded data. On successful return, Certificate.Leaf will be nil because +// the parsed form of the certificate is not retained. +func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (gmtls.Certificate, error) { + fail := func(err error) (gmtls.Certificate, error) { return gmtls.Certificate{}, err } + + var cert gmtls.Certificate + var skippedBlockTypes []string + for { + var certDERBlock *pem.Block + certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) + if certDERBlock == nil { + break + } + if certDERBlock.Type == "CERTIFICATE" { + cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) + } else { + skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type) + } + } + + if len(cert.Certificate) == 0 { + if len(skippedBlockTypes) == 0 { + return fail(errors.New("tls: failed to find any PEM data in certificate input")) + } + if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") { + return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched")) + } + return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) + } + + skippedBlockTypes = skippedBlockTypes[:0] + var keyDERBlock *pem.Block + for { + keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) + if keyDERBlock == nil { + if len(skippedBlockTypes) == 0 { + return fail(errors.New("tls: failed to find any PEM data in key input")) + } + if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" { + return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key")) + } + return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) + } + if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { + break + } + skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) + } + + var err error + cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) if err != nil { - return err + return fail(err) } - certs := client.ConnectionState().PeerCertificates - opts := x509.VerifyOptions{ - DNSName: client.ConnectionState().ServerName, - Intermediates: x509.NewCertPool(), - Roots: tlsConf.RootCAs, + + // We don't need to parse the public key for TLS, but we so do anyway + // to check that it looks sane and matches the private key. + x509Cert, err := gmx509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return fail(err) } - for i, cert := range certs { - if i == 0 { - continue + + switch pub := x509Cert.PublicKey.(type) { + case *rsa.PublicKey: + priv, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + return fail(errors.New("tls: private key type does not match public key type")) + } + if pub.N.Cmp(priv.N) != 0 { + return fail(errors.New("tls: private key does not match public key")) } - opts.Intermediates.AddCert(cert) + case *ecdsa.PublicKey: + pub, _ = x509Cert.PublicKey.(*ecdsa.PublicKey) + switch pub.Curve { + case sm2.P256Sm2(): + priv, ok := cert.PrivateKey.(*sm2.PrivateKey) + if !ok { + return fail(errors.New("tls: sm2 private key type does not match public key type")) + } + if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { + return fail(errors.New("tls: sm2 private key does not match public key")) + } + default: + priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + return fail(errors.New("tls: private key type does not match public key type")) + } + if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { + return fail(errors.New("tls: private key does not match public key")) + } + } + default: + return fail(errors.New("tls: unknown public key algorithm")) + } + return cert, nil +} + +func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return key, nil + default: + return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") + } + } + if key, err := gmx509.ParseSm2PrivateKey(der); err == nil { + return key, nil + } + if key, err := gmx509.ParsePKCS8UnecryptedPrivateKey(der); err == nil { + return key, nil } - _, err = certs[0].Verify(opts) - return err + return nil, errors.New("tls: failed to parse private key") } diff --git a/ssl_test.go b/ssl_test.go index b96bb32384b6642284d4ca0a801fb89df4888c1b..7300cbae704a2b769398342e1b589f5e42d61e4e 100644 --- a/ssl_test.go +++ b/ssl_test.go @@ -4,11 +4,11 @@ package pq import ( _ "crypto/sha256" - "crypto/tls" "crypto/x509" "database/sql" "fmt" "github.com/stretchr/testify/assert" + "github.com/tjfoc/gmsm/gmtls" "os" "path/filepath" "testing" @@ -305,7 +305,7 @@ func Test_sslClientCertificates(t *testing.T) { */ type args struct { - tlsConf *tls.Config + tlsConf *gmtls.Config o values } tests := []struct { @@ -325,7 +325,7 @@ func Test_sslClientCertificates(t *testing.T) { { name: "ssl_no_password_sslinline", args: args{ - tlsConf: &tls.Config{}, + tlsConf: &gmtls.Config{}, o: map[string]string{ paramSSLCert: "Certificate:\n Data:\n Version: 1 (0x0)\n Serial Number:\n 28:ad:77:11:75:ae:93:8d:ea:a2:5f:bf:33:e2:24:28:dc:28:c0:e7\n Signature Algorithm: sha256WithRSAEncryption\n Issuer: CN = postgres\n Validity\n Not Before: Feb 11 11:48:27 2022 GMT\n Not After : Feb 11 11:48:27 2023 GMT\n Subject: CN = fenix\n Subject Public Key Info:\n Public Key Algorithm: rsaEncryption\n RSA Public-Key: (4096 bit)\n Modulus:\n 00:bf:ae:99:10:0a:e9:e8:7b:fb:84:f5:82:dc:66:\n 9e:8d:2f:40:12:d7:8a:96:ce:c9:ca:24:99:29:d7:\n 11:ff:5a:39:5c:3b:96:66:3f:55:04:02:66:98:e8:\n 44:65:b3:f9:7b:eb:af:83:35:e5:23:4b:1f:6f:43:\n 14:c0:df:7d:6f:96:f2:a6:18:65:49:71:84:f1:ce:\n 69:c8:50:83:e2:99:5c:f7:63:48:b3:26:0a:66:91:\n dd:91:e4:86:c6:06:5c:7d:ce:33:7e:c8:53:3e:c9:\n 8d:12:5f:e3:52:d1:c7:68:24:11:2e:d7:55:5d:e9:\n e2:36:2d:ff:ef:15:76:77:11:4e:24:db:fc:b6:64:\n f1:e4:ab:17:69:9b:d1:59:39:6c:60:5e:be:1f:0b:\n 2a:db:82:0c:a8:71:74:2a:ef:ac:6c:63:9b:e1:22:\n 8a:d0:29:1d:bd:2d:d3:ed:76:82:04:b5:1d:c5:66:\n 17:10:fc:3f:ff:9a:7b:0c:61:b2:86:f9:19:3a:c4:\n e1:e4:e9:6e:67:04:c8:72:3a:bc:c1:b4:3a:33:c9:\n 9f:29:db:3c:c4:04:0e:f7:72:45:19:16:1e:e1:b8:\n ad:59:e8:8a:65:64:d4:7f:8f:07:65:03:98:16:fa:\n e6:7d:cd:f5:01:57:b4:05:2b:1d:bb:02:04:a0:57:\n f4:0c:c5:db:45:8a:46:64:31:3a:4d:c9:94:57:f0:\n 45:37:72:61:16:8c:9e:31:39:af:4d:0f:f0:6a:88:\n 8f:a5:9b:88:8f:40:02:98:0f:7c:8d:ae:99:fb:58:\n 98:99:6c:8f:a2:de:ac:6d:17:ca:99:9c:c9:47:63:\n c5:2b:dc:fd:7a:3e:90:13:b6:57:e4:b3:3c:80:2f:\n bf:e4:22:20:02:6c:2c:09:37:7d:86:ef:85:36:87:\n 41:e0:00:dc:e1:4e:86:e6:7e:0b:2f:fc:23:1c:db:\n b6:ee:75:d9:d7:8e:09:ac:7b:2e:6a:77:2f:03:01:\n fe:14:f5:70:ea:72:62:31:91:25:a7:b1:c0:c5:93:\n 16:2c:72:21:64:89:5f:da:1d:57:bd:fb:7c:45:e2:\n d3:3f:92:3b:80:31:e0:6a:ed:60:8f:ab:9d:77:c5:\n 7d:88:48:92:7f:74:11:2a:6d:f4:20:51:52:26:9a:\n 15:8c:64:f2:2e:96:bd:16:f7:96:ad:e7:36:ca:6b:\n 2c:99:58:5f:97:e8:74:58:57:5d:ff:ec:3a:07:1a:\n b5:bc:92:cb:e4:d0:f6:58:d9:30:4b:ad:57:16:9f:\n c7:72:31:79:97:93:76:de:7c:29:a2:57:fe:20:a5:\n 52:1a:95:47:45:d6:d2:4a:15:28:a0:ae:c4:ac:37:\n 71:cf:37\n Exponent: 65537 (0x10001)\n Signature Algorithm: sha256WithRSAEncryption\n b0:ab:7a:2d:16:de:0a:24:4a:af:f5:3f:d7:d2:ad:8e:60:3e:\n 25:bd:48:60:57:a7:c5:22:82:ae:b1:31:a0:39:48:4e:be:2f:\n b8:91:ce:22:b7:6f:fa:09:e6:5b:91:e8:11:b3:d7:86:0f:01:\n f8:db:50:ba:7f:fb:86:94:49:a4:60:e8:b5:b0:ae:75:35:a9:\n a8:54:57:6e:06:14:52:61:12:4f:07:be:8a:68:6a:a3:bc:79:\n 42:40:ce:2d:52:1f:e0:f9:a6:af:a4:bb:ae:37:bd:46:cd:6d:\n e9:83:c1:e9:ab:4c:da:4c:56:d2:0a:d0:83:71:a2:58:d9:54:\n 9f:92:d3:94:ff:60:69:1d:fb:ce:ce:55:49:be:fe:3f:41:93:\n 2a:c5:97:05:f5:10:74:90:a6:b3:bd:b7:e4:d9:3b:f0:d7:15:\n 9f:d2:e9:eb:10:25:e7:f1:ed:33:25:e6:ca:45:d1:cc:18:48:\n 73:1c:06:e7:1b:80:41:d0:c4:69:fd:4d:a3:a5:88:b6:b1:58:\n ad:2a:27:57:27:15:6d:61:4d:14:cc:7d:b6:ab:6f:00:6e:72:\n 98:5f:85:72:ef:f6:02:01:2c:01:2c:0b:ff:69:fc:18:72:5e:\n 38:c7:a5:6e:8d:b8:21:26:1c:f3:d8:5e:51:00:47:d7:22:08:\n 3f:32:96:48:4c:b3:77:62:fa:c1:d9:d0:6f:41:ff:cd:07:98:\n 53:f0:58:96:fb:26:f3:5b:15:05:2a:ae:19:76:34:79:c2:aa:\n a8:8c:6b:96:88:7c:56:c4:8d:be:74:aa:f8:a7:0f:3a:5d:d2:\n 77:0f:8b:7e:3f:76:ea:c4:48:33:fc:2f:84:a6:54:33:33:6f:\n 33:4d:07:d9:6f:b7:8a:73:0f:aa:82:7f:c4:f1:de:58:1e:55:\n cc:75:af:53:ed:e0:20:8d:68:02:f4:c1:7d:fc:96:6e:f6:1b:\n 3e:79:fa:f3:bb:4f:4e:50:a7:33:de:34:63:2a:0c:58:fc:b6:\n 69:cd:e4:49:6e:34:f2:ef:6b:13:b3:0b:81:13:80:9d:07:a6:\n cd:1c:79:ed:6d:f1:3e:53:9d:f7:df:76:16:08:8d:99:e8:bb:\n ef:e7:82:9a:72:6c:ff:9d:31:d4:ed:bd:1f:c2:73:e3:b1:91:\n c1:ab:5e:7e:f8:1f:be:32:43:e2:46:fa:a2:ac:90:ff:a3:9f:\n bb:f0:af:b6:67:06:02:3b:7e:d2:38:2e:f7:6d:b4:55:41:2a:\n d8:f8:90:3c:06:76:6e:69:0c:39:05:a7:0b:dd:db:dc:65:0f:\n f7:45:92:11:4c:b4:dc:43:72:03:e2:78:61:f6:10:c3:8d:0b:\n 3e:82:d3:97:f4:69:56:b5\n-----BEGIN CERTIFICATE-----\nMIIEqjCCApICFCitdxF1rpON6qJfvzPiJCjcKMDnMA0GCSqGSIb3DQEBCwUAMBMx\nETAPBgNVBAMMCHBvc3RncmVzMB4XDTIyMDIxMTExNDgyN1oXDTIzMDIxMTExNDgy\nN1owEDEOMAwGA1UEAwwFZmVuaXgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\nAoICAQC/rpkQCunoe/uE9YLcZp6NL0AS14qWzsnKJJkp1xH/WjlcO5ZmP1UEAmaY\n6ERls/l766+DNeUjSx9vQxTA331vlvKmGGVJcYTxzmnIUIPimVz3Y0izJgpmkd2R\n5IbGBlx9zjN+yFM+yY0SX+NS0cdoJBEu11Vd6eI2Lf/vFXZ3EU4k2/y2ZPHkqxdp\nm9FZOWxgXr4fCyrbggyocXQq76xsY5vhIorQKR29LdPtdoIEtR3FZhcQ/D//mnsM\nYbKG+Rk6xOHk6W5nBMhyOrzBtDozyZ8p2zzEBA73ckUZFh7huK1Z6IplZNR/jwdl\nA5gW+uZ9zfUBV7QFKx27AgSgV/QMxdtFikZkMTpNyZRX8EU3cmEWjJ4xOa9ND/Bq\niI+lm4iPQAKYD3yNrpn7WJiZbI+i3qxtF8qZnMlHY8Ur3P16PpATtlfkszyAL7/k\nIiACbCwJN32G74U2h0HgANzhTobmfgsv/CMc27buddnXjgmsey5qdy8DAf4U9XDq\ncmIxkSWnscDFkxYsciFkiV/aHVe9+3xF4tM/kjuAMeBq7WCPq513xX2ISJJ/dBEq\nbfQgUVImmhWMZPIulr0W95at5zbKayyZWF+X6HRYV13/7DoHGrW8ksvk0PZY2TBL\nrVcWn8dyMXmXk3befCmiV/4gpVIalUdF1tJKFSigrsSsN3HPNwIDAQABMA0GCSqG\nSIb3DQEBCwUAA4ICAQCwq3otFt4KJEqv9T/X0q2OYD4lvUhgV6fFIoKusTGgOUhO\nvi+4kc4it2/6CeZbkegRs9eGDwH421C6f/uGlEmkYOi1sK51NamoVFduBhRSYRJP\nB76KaGqjvHlCQM4tUh/g+aavpLuuN71GzW3pg8Hpq0zaTFbSCtCDcaJY2VSfktOU\n/2BpHfvOzlVJvv4/QZMqxZcF9RB0kKazvbfk2Tvw1xWf0unrECXn8e0zJebKRdHM\nGEhzHAbnG4BB0MRp/U2jpYi2sVitKidXJxVtYU0UzH22q28AbnKYX4Vy7/YCASwB\nLAv/afwYcl44x6VujbghJhzz2F5RAEfXIgg/MpZITLN3YvrB2dBvQf/NB5hT8FiW\n+ybzWxUFKq4ZdjR5wqqojGuWiHxWxI2+dKr4pw86XdJ3D4t+P3bqxEgz/C+EplQz\nM28zTQfZb7eKcw+qgn/E8d5YHlXMda9T7eAgjWgC9MF9/JZu9hs+efrzu09OUKcz\n3jRjKgxY/LZpzeRJbjTy72sTswuBE4CdB6bNHHntbfE+U53333YWCI2Z6Lvv54Ka\ncmz/nTHU7b0fwnPjsZHBq15++B++MkPiRvqirJD/o5+78K+2ZwYCO37SOC73bbRV\nQSrY+JA8BnZuaQw5BacL3dvcZQ/3RZIRTLTcQ3ID4nhh9hDDjQs+gtOX9GlWtQ==\n-----END CERTIFICATE-----\n", paramSSLKey: "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", @@ -346,7 +346,7 @@ func Test_sslClientCertificates(t *testing.T) { { name: "ssl_password_sslinline", args: args{ - tlsConf: &tls.Config{}, + tlsConf: &gmtls.Config{}, o: map[string]string{ paramSSLCert: "Certificate:\n Data:\n Version: 1 (0x0)\n Serial Number:\n 28:ad:77:11:75:ae:93:8d:ea:a2:5f:bf:33:e2:24:28:dc:28:c0:e8\n Signature Algorithm: sha256WithRSAEncryption\n Issuer: CN = postgres\n Validity\n Not Before: Feb 11 11:51:14 2022 GMT\n Not After : Feb 11 11:51:14 2023 GMT\n Subject: CN = fenix\n Subject Public Key Info:\n Public Key Algorithm: rsaEncryption\n RSA Public-Key: (4096 bit)\n Modulus:\n 00:b2:87:23:b8:ac:93:c9:13:cc:2d:4e:80:f3:07:\n 06:58:5d:d5:ff:97:64:89:34:75:84:c5:84:80:2d:\n 0a:45:26:88:a6:1b:41:ac:80:a1:ea:7e:2f:66:e4:\n e9:bd:1f:12:10:58:50:4b:20:2e:91:88:e7:ea:94:\n 15:7e:6b:9b:51:30:b9:b6:9c:f7:9d:44:35:65:39:\n 65:14:20:b5:24:b4:55:13:8e:40:32:ae:5f:84:15:\n 8a:a6:a4:c8:52:7f:d1:bb:05:00:c0:f9:02:23:eb:\n d6:4f:a3:96:94:33:7b:b3:9a:15:96:83:27:1d:d6:\n e6:42:aa:68:52:28:da:dd:b5:14:e8:3f:fd:1c:92:\n 5f:10:f4:18:cb:d2:c3:57:87:bc:14:89:14:27:e6:\n 15:5b:96:f3:17:ce:a3:c6:60:07:49:16:aa:ae:4b:\n e7:d3:be:ff:e4:3d:ac:5b:5b:6e:e5:ad:6c:6c:c8:\n 95:09:c3:51:da:4a:15:c6:93:70:4c:b0:f8:53:60:\n 02:f0:d1:23:03:ba:e4:e3:50:14:f6:1d:84:e5:13:\n 52:81:2f:d0:f5:7f:90:f1:94:ff:a1:11:6a:b8:4f:\n e2:70:16:d0:cb:7b:04:2e:22:db:c0:87:08:5f:bb:\n fc:2c:8e:09:08:27:b9:ab:40:20:32:2f:ca:f1:51:\n 02:ee:56:76:c1:30:3b:6f:27:ef:22:48:cd:6e:64:\n 29:31:d5:d4:4b:d4:9d:9f:e3:c1:53:54:b7:d5:90:\n 1c:ce:45:3c:71:7f:76:30:b0:e9:0e:b1:69:17:41:\n 3b:b7:30:53:59:79:20:85:9d:80:56:cf:55:ad:4a:\n 78:f5:13:ce:d0:ed:db:0e:b3:5e:66:21:b4:de:e0:\n 1d:e2:52:3d:3d:c2:74:52:fa:78:0b:75:50:6c:db:\n d6:9d:e8:47:6b:78:78:fe:cb:3b:3b:59:7c:6b:b7:\n 1a:55:6a:52:77:4d:84:34:64:72:b6:de:30:e2:bc:\n f8:a8:4c:f7:e8:3f:e4:3a:3b:0f:a7:ca:43:73:09:\n 40:5c:c7:5e:da:49:f7:b2:3e:63:81:6c:c2:1e:38:\n 48:0b:51:54:c7:75:19:dd:a9:95:92:da:d8:d8:4a:\n 11:f1:5c:6f:bf:55:d2:5f:ef:42:c4:c5:7e:78:3f:\n d5:1c:5b:b8:0e:d7:7c:51:fc:39:c1:4c:63:51:7c:\n e0:69:ac:92:c5:ba:9e:7f:5f:b9:a8:49:ac:a9:b5:\n 29:ee:fe:62:e8:1f:c8:e5:96:67:e4:69:85:8b:bb:\n 92:db:74:95:6a:ae:38:fe:a3:61:c3:03:52:7c:a3:\n 0f:f8:e3:72:10:3a:6f:9b:08:72:a4:2f:7f:29:9c:\n 61:4d:a1\n Exponent: 65537 (0x10001)\n Signature Algorithm: sha256WithRSAEncryption\n 3c:2c:a4:3f:34:b5:a7:17:a9:6b:8e:35:08:db:82:91:0b:13:\n 09:5f:d7:6d:3b:ba:da:53:0a:58:84:5d:e8:45:ed:21:eb:f3:\n 79:88:31:3c:dc:1b:02:f6:34:11:6f:fd:8e:1c:a2:9e:14:41:\n 04:1a:0e:23:49:04:a5:77:a3:11:f6:8a:38:19:63:c8:86:26:\n fe:a6:8d:48:37:d8:16:19:bb:6a:b1:51:31:3c:f9:bd:3d:7e:\n af:d6:3d:7e:cd:aa:a9:e8:9c:ec:5b:d7:b2:23:55:fa:cf:d6:\n bc:23:3e:1c:08:39:7a:b5:36:b7:39:ca:07:d0:6e:35:f5:b7:\n 2a:46:2f:62:28:b5:12:ca:06:7f:07:bc:ab:9f:d7:c3:59:d3:\n eb:31:8b:46:54:5b:1d:78:5c:79:5e:53:54:a7:b8:60:a6:9e:\n 1b:44:3f:32:76:f8:12:b8:f4:f9:dd:32:2e:0e:0e:e2:4f:0c:\n 50:c7:5c:dc:aa:42:f8:0d:b9:3b:34:b1:ad:dc:45:7e:33:80:\n 4c:53:4f:ec:9d:36:2f:73:06:aa:fd:f1:44:c2:3f:1f:1f:3b:\n 53:f3:40:f2:f5:76:dc:07:ba:50:96:e7:e5:f1:67:81:a7:56:\n 8f:4d:8e:5e:59:b8:58:10:4a:e1:e4:34:8b:b7:2a:45:7f:e4:\n ff:44:3b:73:1e:3b:64:64:2d:2b:f0:e5:bc:94:50:ce:e0:05:\n 5a:1a:44:4d:3d:8d:1e:0c:18:13:be:9d:ac:37:bd:59:a1:72:\n 53:f3:f9:3c:fa:4e:5d:37:1c:ea:69:68:26:52:64:05:76:b0:\n 2c:d2:30:d5:7d:98:3c:5b:ef:18:26:69:d8:0a:bd:ee:04:f8:\n 02:f7:66:5b:07:14:b6:e6:5f:c1:3d:8a:cb:c0:12:15:c4:33:\n ad:cf:67:e8:f0:31:77:79:90:77:c0:5e:37:72:5b:e0:08:bf:\n b9:f3:06:0d:ec:68:83:22:50:ca:73:78:1e:f4:90:35:32:b0:\n af:64:7b:b7:54:44:4c:fe:72:43:11:f6:b6:8c:be:aa:19:5d:\n c8:9d:75:5f:97:a7:7f:e7:36:27:39:98:a9:77:d3:fe:bf:2b:\n db:56:c2:08:ad:c3:a6:36:e0:9b:fa:41:22:44:2f:55:04:46:\n b7:97:24:cc:51:5b:a2:ed:a4:f9:dd:b6:54:02:2c:42:71:96:\n 6f:ef:eb:52:67:b1:73:de:fc:92:41:9d:f3:e1:87:8e:b4:41:\n 9a:3e:5d:a7:21:c1:de:42:8e:65:1f:9a:c1:86:07:9d:38:9f:\n 81:8e:aa:a5:57:1b:0e:c6:c8:55:4c:a2:9b:34:f5:99:3f:bf:\n bb:69:4d:d7:39:93:13:bd\n-----BEGIN CERTIFICATE-----\nMIIEqjCCApICFCitdxF1rpON6qJfvzPiJCjcKMDoMA0GCSqGSIb3DQEBCwUAMBMx\nETAPBgNVBAMMCHBvc3RncmVzMB4XDTIyMDIxMTExNTExNFoXDTIzMDIxMTExNTEx\nNFowEDEOMAwGA1UEAwwFZmVuaXgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\nAoICAQCyhyO4rJPJE8wtToDzBwZYXdX/l2SJNHWExYSALQpFJoimG0GsgKHqfi9m\n5Om9HxIQWFBLIC6RiOfqlBV+a5tRMLm2nPedRDVlOWUUILUktFUTjkAyrl+EFYqm\npMhSf9G7BQDA+QIj69ZPo5aUM3uzmhWWgycd1uZCqmhSKNrdtRToP/0ckl8Q9BjL\n0sNXh7wUiRQn5hVblvMXzqPGYAdJFqquS+fTvv/kPaxbW27lrWxsyJUJw1HaShXG\nk3BMsPhTYALw0SMDuuTjUBT2HYTlE1KBL9D1f5DxlP+hEWq4T+JwFtDLewQuItvA\nhwhfu/wsjgkIJ7mrQCAyL8rxUQLuVnbBMDtvJ+8iSM1uZCkx1dRL1J2f48FTVLfV\nkBzORTxxf3YwsOkOsWkXQTu3MFNZeSCFnYBWz1WtSnj1E87Q7dsOs15mIbTe4B3i\nUj09wnRS+ngLdVBs29ad6EdreHj+yzs7WXxrtxpValJ3TYQ0ZHK23jDivPioTPfo\nP+Q6Ow+nykNzCUBcx17aSfeyPmOBbMIeOEgLUVTHdRndqZWS2tjYShHxXG+/VdJf\n70LExX54P9UcW7gO13xR/DnBTGNRfOBprJLFup5/X7moSayptSnu/mLoH8jllmfk\naYWLu5LbdJVqrjj+o2HDA1J8ow/443IQOm+bCHKkL38pnGFNoQIDAQABMA0GCSqG\nSIb3DQEBCwUAA4ICAQA8LKQ/NLWnF6lrjjUI24KRCxMJX9dtO7raUwpYhF3oRe0h\n6/N5iDE83BsC9jQRb/2OHKKeFEEEGg4jSQSld6MR9oo4GWPIhib+po1IN9gWGbtq\nsVExPPm9PX6v1j1+zaqp6JzsW9eyI1X6z9a8Iz4cCDl6tTa3OcoH0G419bcqRi9i\nKLUSygZ/B7yrn9fDWdPrMYtGVFsdeFx5XlNUp7hgpp4bRD8ydvgSuPT53TIuDg7i\nTwxQx1zcqkL4Dbk7NLGt3EV+M4BMU0/snTYvcwaq/fFEwj8fHztT80Dy9XbcB7pQ\nlufl8WeBp1aPTY5eWbhYEErh5DSLtypFf+T/RDtzHjtkZC0r8OW8lFDO4AVaGkRN\nPY0eDBgTvp2sN71ZoXJT8/k8+k5dNxzqaWgmUmQFdrAs0jDVfZg8W+8YJmnYCr3u\nBPgC92ZbBxS25l/BPYrLwBIVxDOtz2fo8DF3eZB3wF43clvgCL+58wYN7GiDIlDK\nc3ge9JA1MrCvZHu3VERM/nJDEfa2jL6qGV3InXVfl6d/5zYnOZipd9P+vyvbVsII\nrcOmNuCb+kEiRC9VBEa3lyTMUVui7aT53bZUAixCcZZv7+tSZ7Fz3vySQZ3z4YeO\ntEGaPl2nIcHeQo5lH5rBhgedOJ+BjqqlVxsOxshVTKKbNPWZP7+7aU3XOZMTvQ==\n-----END CERTIFICATE-----\n", paramSSLKey: "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,CB468F1E3982C588\n\n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\n-----END RSA PRIVATE KEY-----\n",