diff options
Diffstat (limited to 'vendor/github.com/lib/pq')
-rwxr-xr-x | vendor/github.com/lib/pq/.travis.sh | 12 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/.travis.yml | 16 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/README.md | 13 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/TESTS.md | 33 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/bench_test.go | 5 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/conn.go | 39 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/conn_go18.go | 5 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/conn_test.go | 67 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/connector.go | 43 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/connector_example_test.go | 33 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/connector_test.go | 67 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/copy_test.go | 15 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/encode_test.go | 27 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/error.go | 9 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/go18_test.go | 4 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/notify.go | 6 | ||||
-rw-r--r-- | vendor/github.com/lib/pq/ssl.go | 57 |
17 files changed, 351 insertions, 100 deletions
diff --git a/vendor/github.com/lib/pq/.travis.sh b/vendor/github.com/lib/pq/.travis.sh index ead01df..a297dc4 100755 --- a/vendor/github.com/lib/pq/.travis.sh +++ b/vendor/github.com/lib/pq/.travis.sh @@ -71,12 +71,6 @@ postgresql_uninstall() { } megacheck_install() { - # Megacheck is Go 1.6+, so skip if Go 1.5. - if [[ "$(go version)" =~ "go1.5" ]] - then - echo "megacheck not supported, skipping installation" - return 0 - fi # Lock megacheck version at $MEGACHECK_VERSION to prevent spontaneous # new error messages in old code. go get -d honnef.co/go/tools/... @@ -86,12 +80,6 @@ megacheck_install() { } golint_install() { - # Golint is Go 1.6+, so skip if Go 1.5. - if [[ "$(go version)" =~ "go1.5" ]] - then - echo "golint not supported, skipping installation" - return 0 - fi go get github.com/golang/lint/golint } diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml index 4e34e88..18556e0 100644 --- a/vendor/github.com/lib/pq/.travis.yml +++ b/vendor/github.com/lib/pq/.travis.yml @@ -1,11 +1,9 @@ language: go go: - - 1.5.x - - 1.6.x - - 1.7.x - 1.8.x - 1.9.x + - 1.10.x - master sudo: true @@ -16,7 +14,7 @@ env: - PQGOSSLTESTS=1 - PQSSLCERTTEST_PATH=$PWD/certs - PGHOST=127.0.0.1 - - MEGACHECK_VERSION=2017.2.1 + - MEGACHECK_VERSION=2017.2.2 matrix: - PGVERSION=10 - PGVERSION=9.6 @@ -46,13 +44,7 @@ script: - > goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }' - go vet ./... - # For compatibility with Go 1.5, launch only if megacheck is present. - - > - which megacheck > /dev/null && megacheck -go 1.5 ./... - || echo 'megacheck is not supported, skipping check' - # For compatibility with Go 1.5, launch only if golint is present. - - > - which golint > /dev/null && golint ./... - || echo 'golint is not supported, skipping check' + - megacheck -go 1.8 ./... + - golint ./... - PQTEST_BINARY_PARAMETERS=no go test -race -v ./... - PQTEST_BINARY_PARAMETERS=yes go test -race -v ./... diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md index 781c89e..d71f3c2 100644 --- a/vendor/github.com/lib/pq/README.md +++ b/vendor/github.com/lib/pq/README.md @@ -14,18 +14,7 @@ documentation at <http://godoc.org/github.com/lib/pq>. ## Tests -`go test` is used for testing. A running PostgreSQL server is -required, with the ability to log in. The default database to connect -to test with is "pqgotest," but it can be overridden using environment -variables. - -Example: - - PGHOST=/run/postgresql go test github.com/lib/pq - -Optionally, a benchmark suite can be run as part of the tests: - - PGHOST=/run/postgresql go test -bench . +`go test` is used for testing. See [TESTS.md](TESTS.md) for more details. ## Features diff --git a/vendor/github.com/lib/pq/TESTS.md b/vendor/github.com/lib/pq/TESTS.md new file mode 100644 index 0000000..f050211 --- /dev/null +++ b/vendor/github.com/lib/pq/TESTS.md @@ -0,0 +1,33 @@ +# Tests + +## Running Tests + +`go test` is used for testing. A running PostgreSQL +server is required, with the ability to log in. The +database to connect to test with is "pqgotest," on +"localhost" but these can be overridden using [environment +variables](https://www.postgresql.org/docs/9.3/static/libpq-envars.html). + +Example: + + PGHOST=/run/postgresql go test + +## Benchmarks + +A benchmark suite can be run as part of the tests: + + go test -bench . + +## Example setup (Docker) + +Run a postgres container: + +``` +docker run --expose 5432:5432 postgres +``` + +Run tests: + +``` +PGHOST=localhost PGPORT=5432 PGUSER=postgres PGSSLMODE=disable PGDATABASE=postgres go test +``` diff --git a/vendor/github.com/lib/pq/bench_test.go b/vendor/github.com/lib/pq/bench_test.go index e71f41d..33d7a02 100644 --- a/vendor/github.com/lib/pq/bench_test.go +++ b/vendor/github.com/lib/pq/bench_test.go @@ -5,6 +5,7 @@ package pq import ( "bufio" "bytes" + "context" "database/sql" "database/sql/driver" "io" @@ -156,7 +157,7 @@ func benchMockQuery(b *testing.B, c *conn, query string) { b.Fatal(err) } defer stmt.Close() - rows, err := stmt.Query(nil) + rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil) if err != nil { b.Fatal(err) } @@ -266,7 +267,7 @@ func BenchmarkMockPreparedSelectSeries(b *testing.B) { } func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) { - rows, err := stmt.Query(nil) + rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil) if err != nil { b.Fatal(err) } diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go index fadb88e..43c8df2 100644 --- a/vendor/github.com/lib/pq/conn.go +++ b/vendor/github.com/lib/pq/conn.go @@ -339,7 +339,20 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { if err != nil { return nil, err } - cn.ssl(o) + + err = cn.ssl(o) + if err != nil { + return nil, err + } + + // cn.startup panics on error. Make sure we don't leak cn.c. + panicking := true + defer func() { + if panicking { + cn.c.Close() + } + }() + cn.buf = bufio.NewReader(cn.c) cn.startup(o) @@ -347,6 +360,7 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { err = cn.c.SetDeadline(time.Time{}) } + panicking = false return cn, err } @@ -1019,30 +1033,35 @@ func (cn *conn) recv1() (t byte, r *readBuf) { return t, r } -func (cn *conn) ssl(o values) { - upgrade := ssl(o) +func (cn *conn) ssl(o values) error { + upgrade, err := ssl(o) + if err != nil { + return err + } + if upgrade == nil { // Nothing to do - return + return nil } w := cn.writeBuf(0) w.int32(80877103) - if err := cn.sendStartupPacket(w); err != nil { - panic(err) + if err = cn.sendStartupPacket(w); err != nil { + return err } b := cn.scratch[:1] - _, err := io.ReadFull(cn.c, b) + _, err = io.ReadFull(cn.c, b) if err != nil { - panic(err) + return err } if b[0] != 'S' { - panic(ErrSSLNotSupported) + return ErrSSLNotSupported } - cn.c = upgrade(cn.c) + cn.c, err = upgrade(cn.c) + return err } // isDriverSetting returns true iff a setting is purely for configuring the diff --git a/vendor/github.com/lib/pq/conn_go18.go b/vendor/github.com/lib/pq/conn_go18.go index ab97a10..a5254f2 100644 --- a/vendor/github.com/lib/pq/conn_go18.go +++ b/vendor/github.com/lib/pq/conn_go18.go @@ -108,7 +108,10 @@ func (cn *conn) cancel() error { can := conn{ c: c, } - can.ssl(cn.opts) + err = can.ssl(cn.opts) + if err != nil { + return err + } w := can.writeBuf(0) w.int32(80877102) // cancel request code diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go index 030a798..e654b85 100644 --- a/vendor/github.com/lib/pq/conn_test.go +++ b/vendor/github.com/lib/pq/conn_test.go @@ -1,6 +1,7 @@ package pq import ( + "context" "database/sql" "database/sql/driver" "fmt" @@ -28,7 +29,7 @@ func forceBinaryParameters() bool { } } -func openTestConnConninfo(conninfo string) (*sql.DB, error) { +func testConninfo(conninfo string) string { defaultTo := func(envvar string, value string) { if os.Getenv(envvar) == "" { os.Setenv(envvar, value) @@ -43,8 +44,11 @@ func openTestConnConninfo(conninfo string) (*sql.DB, error) { !strings.HasPrefix(conninfo, "postgresql://") { conninfo = conninfo + " binary_parameters=yes" } + return conninfo +} - return sql.Open("postgres", conninfo) +func openTestConnConninfo(conninfo string) (*sql.DB, error) { + return sql.Open("postgres", testConninfo(conninfo)) } func openTestConn(t Fatalistic) *sql.DB { @@ -637,6 +641,57 @@ func TestErrorDuringStartup(t *testing.T) { } } +type testConn struct { + closed bool + net.Conn +} + +func (c *testConn) Close() error { + c.closed = true + return c.Conn.Close() +} + +type testDialer struct { + conns []*testConn +} + +func (d *testDialer) Dial(ntw, addr string) (net.Conn, error) { + c, err := net.Dial(ntw, addr) + if err != nil { + return nil, err + } + tc := &testConn{Conn: c} + d.conns = append(d.conns, tc) + return tc, nil +} + +func (d *testDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) { + c, err := net.DialTimeout(ntw, addr, timeout) + if err != nil { + return nil, err + } + tc := &testConn{Conn: c} + d.conns = append(d.conns, tc) + return tc, nil +} + +func TestErrorDuringStartupClosesConn(t *testing.T) { + // Don't use the normal connection setup, this is intended to + // blow up in the startup packet from a non-existent user. + var d testDialer + c, err := DialOpen(&d, testConninfo("user=thisuserreallydoesntexist")) + if err == nil { + c.Close() + t.Fatal("expected dial error") + } + if len(d.conns) != 1 { + t.Fatalf("got len(d.conns) = %d, want = %d", len(d.conns), 1) + } + if !d.conns[0].closed { + t.Error("connection leaked") + } +} + func TestBadConn(t *testing.T) { var err error @@ -1209,8 +1264,8 @@ func TestParseComplete(t *testing.T) { // Test interface conformance. var ( - _ driver.Execer = (*conn)(nil) - _ driver.Queryer = (*conn)(nil) + _ driver.ExecerContext = (*conn)(nil) + _ driver.QueryerContext = (*conn)(nil) ) func TestNullAfterNonNull(t *testing.T) { @@ -1555,10 +1610,10 @@ func TestRowsResultTag(t *testing.T) { t.Fatal(err) } defer conn.Close() - q := conn.(driver.Queryer) + q := conn.(driver.QueryerContext) for _, test := range tests { - if rows, err := q.Query(test.query, nil); err != nil { + if rows, err := q.QueryContext(context.Background(), test.query, nil); err != nil { t.Fatalf("%s: %s", test.query, err) } else { r := rows.(ResultTag) diff --git a/vendor/github.com/lib/pq/connector.go b/vendor/github.com/lib/pq/connector.go new file mode 100644 index 0000000..9e66eb5 --- /dev/null +++ b/vendor/github.com/lib/pq/connector.go @@ -0,0 +1,43 @@ +// +build go1.10 + +package pq + +import ( + "context" + "database/sql/driver" +) + +// Connector represents a fixed configuration for the pq driver with a given +// name. Connector satisfies the database/sql/driver Connector interface and +// can be used to create any number of DB Conn's via the database/sql OpenDB +// function. +// +// See https://golang.org/pkg/database/sql/driver/#Connector. +// See https://golang.org/pkg/database/sql/#OpenDB. +type connector struct { + name string +} + +// Connect returns a connection to the database using the fixed configuration +// of this Connector. Context is not used. +func (c *connector) Connect(_ context.Context) (driver.Conn, error) { + return (&Driver{}).Open(c.name) +} + +// Driver returnst the underlying driver of this Connector. +func (c *connector) Driver() driver.Driver { + return &Driver{} +} + +var _ driver.Connector = &connector{} + +// NewConnector returns a connector for the pq driver in a fixed configuration +// with the given name. The returned connector can be used to create any number +// of equivalent Conn's. The returned connector is intended to be used with +// database/sql.OpenDB. +// +// See https://golang.org/pkg/database/sql/driver/#Connector. +// See https://golang.org/pkg/database/sql/#OpenDB. +func NewConnector(name string) (driver.Connector, error) { + return &connector{name: name}, nil +} diff --git a/vendor/github.com/lib/pq/connector_example_test.go b/vendor/github.com/lib/pq/connector_example_test.go new file mode 100644 index 0000000..5b66cf4 --- /dev/null +++ b/vendor/github.com/lib/pq/connector_example_test.go @@ -0,0 +1,33 @@ +// +build go1.10 + +package pq_test + +import ( + "database/sql" + "fmt" + + "github.com/lib/pq" +) + +func ExampleNewConnector() { + name := "" + connector, err := pq.NewConnector(name) + if err != nil { + fmt.Println(err) + return + } + db := sql.OpenDB(connector) + if err != nil { + fmt.Println(err) + return + } + defer db.Close() + + // Use the DB + txn, err := db.Begin() + if err != nil { + fmt.Println(err) + return + } + txn.Rollback() +} diff --git a/vendor/github.com/lib/pq/connector_test.go b/vendor/github.com/lib/pq/connector_test.go new file mode 100644 index 0000000..3d2c67b --- /dev/null +++ b/vendor/github.com/lib/pq/connector_test.go @@ -0,0 +1,67 @@ +// +build go1.10 + +package pq + +import ( + "context" + "database/sql" + "database/sql/driver" + "testing" +) + +func TestNewConnector_WorksWithOpenDB(t *testing.T) { + name := "" + c, err := NewConnector(name) + if err != nil { + t.Fatal(err) + } + db := sql.OpenDB(c) + defer db.Close() + // database/sql might not call our Open at all unless we do something with + // the connection + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + txn.Rollback() +} + +func TestNewConnector_Connect(t *testing.T) { + name := "" + c, err := NewConnector(name) + if err != nil { + t.Fatal(err) + } + db, err := c.Connect(context.Background()) + if err != nil { + t.Fatal(err) + } + defer db.Close() + // database/sql might not call our Open at all unless we do something with + // the connection + txn, err := db.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{}) + if err != nil { + t.Fatal(err) + } + txn.Rollback() +} + +func TestNewConnector_Driver(t *testing.T) { + name := "" + c, err := NewConnector(name) + if err != nil { + t.Fatal(err) + } + db, err := c.Driver().Open(name) + if err != nil { + t.Fatal(err) + } + defer db.Close() + // database/sql might not call our Open at all unless we do something with + // the connection + txn, err := db.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{}) + if err != nil { + t.Fatal(err) + } + txn.Rollback() +} diff --git a/vendor/github.com/lib/pq/copy_test.go b/vendor/github.com/lib/pq/copy_test.go index c1a3cd7..a888a89 100644 --- a/vendor/github.com/lib/pq/copy_test.go +++ b/vendor/github.com/lib/pq/copy_test.go @@ -4,6 +4,7 @@ import ( "bytes" "database/sql" "database/sql/driver" + "net" "strings" "testing" ) @@ -400,15 +401,19 @@ func TestCopyRespLoopConnectionError(t *testing.T) { if err == nil { t.Fatalf("expected error") } - pge, ok := err.(*Error) - if !ok { + switch pge := err.(type) { + case *Error: + if pge.Code.Name() != "admin_shutdown" { + t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) + } + case *net.OpError: + // ignore + default: if err == driver.ErrBadConn { // likely an EPIPE } else { - t.Fatalf("expected *pq.Error or driver.ErrBadConn, got %+#v", err) + t.Fatalf("unexpected error, got %+#v", err) } - } else if pge.Code.Name() != "admin_shutdown" { - t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) } _ = stmt.Close() diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go index 634a05c..d58798a 100644 --- a/vendor/github.com/lib/pq/encode_test.go +++ b/vendor/github.com/lib/pq/encode_test.go @@ -4,7 +4,7 @@ import ( "bytes" "database/sql" "fmt" - "strings" + "regexp" "testing" "time" @@ -304,24 +304,27 @@ func TestInfinityTimestamp(t *testing.T) { var err error var resultT time.Time - expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported` + expectedErrorStrRegexp := regexp.MustCompile( + `^sql: Scan error on column index 0(, name "timestamp(tz)?"|): unsupported`) + type testCases []struct { - Query string - Param string - ExpectedErrStrPrefix string - ExpectedVal interface{} + Query string + Param string + ExpectedErrorStrRegexp *regexp.Regexp + ExpectedVal interface{} } tc := testCases{ - {"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"}, - {"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"}, - {"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"}, - {"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"}, + {"SELECT $1::timestamp", "-infinity", expectedErrorStrRegexp, "-infinity"}, + {"SELECT $1::timestamptz", "-infinity", expectedErrorStrRegexp, "-infinity"}, + {"SELECT $1::timestamp", "infinity", expectedErrorStrRegexp, "infinity"}, + {"SELECT $1::timestamptz", "infinity", expectedErrorStrRegexp, "infinity"}, } // try to assert []byte to time.Time for _, q := range tc { err = db.QueryRow(q.Query, q.Param).Scan(&resultT) - if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) { - t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err) + if !q.ExpectedErrorStrRegexp.MatchString(err.Error()) { + t.Errorf("Scanning -/+infinity, expected error to match regexp %q, got %q", + q.ExpectedErrorStrRegexp, err) } } // yield []byte diff --git a/vendor/github.com/lib/pq/error.go b/vendor/github.com/lib/pq/error.go index b4bb44c..96aae29 100644 --- a/vendor/github.com/lib/pq/error.go +++ b/vendor/github.com/lib/pq/error.go @@ -153,6 +153,7 @@ var errorCodeNames = map[ErrorCode]string{ "22004": "null_value_not_allowed", "22002": "null_value_no_indicator_parameter", "22003": "numeric_value_out_of_range", + "2200H": "sequence_generator_limit_exceeded", "22026": "string_data_length_mismatch", "22001": "string_data_right_truncation", "22011": "substring_error", @@ -459,6 +460,11 @@ func errorf(s string, args ...interface{}) { panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) } +// TODO(ainar-g) Rename to errorf after removing panics. +func fmterrorf(s string, args ...interface{}) error { + return fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)) +} + func errRecoverNoErrBadConn(err *error) { e := recover() if e == nil { @@ -487,7 +493,8 @@ func (c *conn) errRecover(err *error) { *err = v } case *net.OpError: - *err = driver.ErrBadConn + c.bad = true + *err = v case error: if v == io.EOF || v.(error).Error() == "remote error: handshake failure" { *err = driver.ErrBadConn diff --git a/vendor/github.com/lib/pq/go18_test.go b/vendor/github.com/lib/pq/go18_test.go index 4bf6391..1a88a5b 100644 --- a/vendor/github.com/lib/pq/go18_test.go +++ b/vendor/github.com/lib/pq/go18_test.go @@ -228,7 +228,9 @@ func TestContextCancelBegin(t *testing.T) { cancel() if err != nil { t.Fatal(err) - } else if err := tx.Rollback(); err != nil && err != sql.ErrTxDone { + } else if err := tx.Rollback(); err != nil && + err.Error() != "pq: canceling statement due to user request" && + err != sql.ErrTxDone { t.Fatal(err) } }() diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go index 412c6ac..947d189 100644 --- a/vendor/github.com/lib/pq/notify.go +++ b/vendor/github.com/lib/pq/notify.go @@ -637,7 +637,7 @@ func (l *Listener) disconnectCleanup() error { // after the connection has been established. func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error { doneChan := make(chan error) - go func() { + go func(notificationChan <-chan *Notification) { for channel := range l.channels { // If we got a response, return that error to our caller as it's // going to be more descriptive than cn.Err(). @@ -658,7 +658,7 @@ func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notificatio } } doneChan <- nil - }() + }(notificationChan) // Ignore notifications while synchronization is going on to avoid // deadlocks. We have to send a nil notification over Notify anyway as @@ -784,7 +784,7 @@ func (l *Listener) listenerConnLoop() { } l.emitEvent(ListenerEventDisconnected, err) - time.Sleep(nextReconnect.Sub(time.Now())) + time.Sleep(time.Until(nextReconnect)) } } diff --git a/vendor/github.com/lib/pq/ssl.go b/vendor/github.com/lib/pq/ssl.go index 7deb304..e1a326a 100644 --- a/vendor/github.com/lib/pq/ssl.go +++ b/vendor/github.com/lib/pq/ssl.go @@ -12,7 +12,7 @@ import ( // 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 { +func ssl(o values) (func(net.Conn) (net.Conn, error), error) { verifyCaOnly := false tlsConf := tls.Config{} switch mode := o["sslmode"]; mode { @@ -45,29 +45,38 @@ func ssl(o values) func(net.Conn) net.Conn { case "verify-full": tlsConf.ServerName = o["host"] case "disable": - return nil + return nil, nil default: - errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) + return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) } - sslClientCertificates(&tlsConf, o) - sslCertificateAuthority(&tlsConf, o) + err := sslClientCertificates(&tlsConf, o) + if err != nil { + return nil, err + } + err = sslCertificateAuthority(&tlsConf, o) + if err != nil { + return nil, err + } sslRenegotiation(&tlsConf) - return func(conn net.Conn) net.Conn { + return func(conn net.Conn) (net.Conn, error) { client := tls.Client(conn, &tlsConf) if verifyCaOnly { - sslVerifyCertificateAuthority(client, &tlsConf) + err := sslVerifyCertificateAuthority(client, &tlsConf) + if err != nil { + return nil, err + } } - return client - } + 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) { +func sslClientCertificates(tlsConf *tls.Config, o values) error { // user.Current() might fail when cross-compiling. We have to ignore the // error and continue without home directory defaults, since we wouldn't // know from where to load them. @@ -82,13 +91,13 @@ func sslClientCertificates(tlsConf *tls.Config, o values) { } // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045 if len(sslcert) == 0 { - return + return nil } // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054 if _, err := os.Stat(sslcert); os.IsNotExist(err) { - return + return nil } else if err != nil { - panic(err) + return err } // In libpq, the ssl key is only loaded if the setting is not blank. @@ -101,19 +110,21 @@ func sslClientCertificates(tlsConf *tls.Config, o values) { if len(sslkey) > 0 { if err := sslKeyPermissions(sslkey); err != nil { - panic(err) + return err } } cert, err := tls.LoadX509KeyPair(sslcert, sslkey) if err != nil { - panic(err) + return err } + tlsConf.Certificates = []tls.Certificate{cert} + return nil } // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. -func sslCertificateAuthority(tlsConf *tls.Config, o values) { +func sslCertificateAuthority(tlsConf *tls.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 @@ -122,22 +133,24 @@ func sslCertificateAuthority(tlsConf *tls.Config, o values) { cert, err := ioutil.ReadFile(sslrootcert) if err != nil { - panic(err) + return err } if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { - errorf("couldn't parse pem in sslrootcert") + return fmterrorf("couldn't parse pem in sslrootcert") } } + + 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) { +func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error { err := client.Handshake() if err != nil { - panic(err) + return err } certs := client.ConnectionState().PeerCertificates opts := x509.VerifyOptions{ @@ -152,7 +165,5 @@ func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) { opts.Intermediates.AddCert(cert) } _, err = certs[0].Verify(opts) - if err != nil { - panic(err) - } + return err } |