Compare commits

...

19 Commits
xorm ... master

Author SHA1 Message Date
5e5a835792 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-01 12:01:46 +02:00
2523b32d19 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-17 13:17:55 +01:00
2967465193 replaced ioutil with io
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-02 17:10:03 +01:00
73fcd296fd updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-10-30 16:15:56 +01:00
4d4e6c7970 updated .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-26 12:04:36 +01:00
961f83badb fix with defers on failure with tor
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-03-09 00:04:23 +01:00
d05d283354 hotfix in logging strings
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
continuous-integration/drone Build is passing
2021-10-15 12:38:07 +02:00
1fd09cb653 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-15 12:17:51 +02:00
1f07c1562d removed golang version from drone-ci
All checks were successful
continuous-integration/drone/push Build is passing
2021-08-30 19:38:55 +02:00
4eaedbfef6 updated dependencies with golang version fixed to 1.17
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2021-08-29 20:37:36 +02:00
f3a2d6486c fixed tor session kill bug
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2021-06-01 21:45:08 +02:00
f13551a529 updated gitignore
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-01 21:33:14 +02:00
d4db887d5a updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-07 11:19:25 +02:00
498ad25ccd updated README.md
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-13 20:42:42 +01:00
09432848ce updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-04 20:50:17 +01:00
d9445d0deb updated README.md
All checks were successful
continuous-integration/drone/push Build is passing
2020-11-14 13:15:15 +01:00
3db988b553 update drone related files
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2020-11-14 13:09:58 +01:00
fb71e75ef5 removed arm64 port
All checks were successful
continuous-integration/drone/push Build is passing
2020-11-14 13:00:23 +01:00
0a6ddb4d71 added support for tor network in master dev branch
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2020-11-14 12:22:23 +01:00
179 changed files with 14018 additions and 1056 deletions

View File

@ -1,42 +1,74 @@
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: default-linux-amd64 name: build-linux
steps: steps:
- name: build - name: build-linux-amd64
image: golang image: golang
commands: commands:
- ./ci-build.sh build - go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
environment: environment:
GOOS: linux GOOS: linux
GOARCH: amd64 GOARCH: amd64
- name: release GOOPTIONS: -mod=vendor
image: plugins/gitea-release SRCFILES: cmd/coronafana/*.go
settings: PROJECTNAME: coronafana
base_url: https://git.paulbsd.com
api_key:
from_secret: gitea_token
files: "*.tar.gz"
checksum:
- sha256
- sha512
when: when:
event: tag event:
exclude:
- tag
- name: build-linux-arm64
image: golang
commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
environment:
GOOS: linux
GOARCH: arm64
GOOPTIONS: -mod=vendor
SRCFILES: cmd/coronafana/*.go
PROJECTNAME: coronafana
when:
event:
exclude:
- tag
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: default-linux-arm64 name: gitea-release-linux
steps: steps:
- name: build - name: build-linux-amd64
image: golang image: golang
commands: commands:
- ./ci-build.sh build - go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment:
GOOS: linux
GOARCH: amd64
GOOPTIONS: -mod=vendor
SRCFILES: cmd/coronafana/*.go
PROJECTNAME: coronafana
when:
event:
- tag
- name: build-linux-arm64
image: golang
commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment: environment:
GOOS: linux GOOS: linux
GOARCH: arm64 GOARCH: arm64
GOOPTIONS: -mod=vendor
SRCFILES: cmd/coronafana/*.go
PROJECTNAME: coronafana
when:
event:
- tag
- name: release - name: release
image: plugins/gitea-release image: plugins/gitea-release
settings: settings:
@ -47,5 +79,7 @@ steps:
checksum: checksum:
- sha256 - sha256
- sha512 - sha512
title: VERSION
when: when:
event: tag event:
- tag

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
/*.ini /*.ini
/*.json /*.json
/dist /dist
/data-dir*
*.swp

View File

@ -1,19 +0,0 @@
# coronafana Makefile
GOCMD=go
GOBUILDCMD=${GOCMD} build
GOOPTIONS=-mod=vendor -ldflags="-s -w"
PACKRCMD=${GOPATH}/bin/packr2
RMCMD=rm
BINNAME=coronafana
SRCFILES=cmd/coronafana/*.go
all: build
build:
${GOBUILDCMD} ${GOOPTIONS} ${SRCFILES}
clean:
${RMCMD} -f ${BINNAME}

View File

@ -1,4 +1,5 @@
# coronafana # coronafana
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/coronafana/status.svg)](https://drone.paulbsd.com/paulbsd/coronafana)
## Summary ## Summary
@ -35,7 +36,7 @@ corona_url="https://coronavirus.politologue.com/data/coronavirus/coronacsv.aspx?
## License ## License
```text ```text
Copyright (c) 2020, PaulBSD Copyright (c) 2020 PaulBSD
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -1,44 +0,0 @@
#!/bin/bash
PROJECTNAME=coronafana
RELEASENAME=${PROJECTNAME}
VERSION="0"
GOOPTIONS="-mod=vendor"
SRCFILES=cmd/coronafana/*.go
build() {
if [[ ! -z $DRONE_TAG ]]
then
VERSION=$DRONE_TAG
elif [[ ! -z $DRONE_TAG ]]
then
VERSION=$DRONE_COMMIT
fi
if [[ ! -z $VERSION && ! -z $GOOS && ! -z $GOARCH ]]
then
RELEASENAME=${PROJECTNAME}-${VERSION}-${GOOS}-${GOARCH}
fi
go build -o ${PROJECTNAME} ${GOOPTIONS} ${SRCFILES}
if [[ ! -z $DRONE_TAG ]]
then
tar -czvf ${RELEASENAME}.tar.gz ${PROJECTNAME}
fi
rm ${PROJECTNAME}
}
clean() {
rm -rf $RELEASEDIR
}
case $1 in
"build")
build
;;
"clean")
clean
;;
*)
;;
esac

View File

@ -6,6 +6,7 @@ import (
"git.paulbsd.com/paulbsd/coronafana/src/config" "git.paulbsd.com/paulbsd/coronafana/src/config"
"git.paulbsd.com/paulbsd/coronafana/src/coronafana" "git.paulbsd.com/paulbsd/coronafana/src/coronafana"
"git.paulbsd.com/paulbsd/coronafana/src/network"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
@ -27,39 +28,54 @@ func main() {
cfg.DbHostname, cfg.DbHostname,
cfg.DbName)) cfg.DbName))
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
_, err = db.Exec(cfg.DbSchemaGlobal) _, err = db.Exec(cfg.DbSchemaGlobal)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
_, err = db.Exec(cfg.DbSchemaPays) _, err = db.Exec(cfg.DbSchemaPays)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
cr, err = coronafana.GetData(cfg) tr, dialer, dialerctx, err := network.InitTorSession()
defer dialerctx()
defer tr.Close()
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
}
cr, err = coronafana.GetData(cfg, dialer)
if err != nil {
log.Println(err)
return
} }
err = cr.GetMaxDates(cfg, *db) err = cr.GetMaxDates(cfg, *db)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
// Processes data for 'Global' // Processes data for 'Global'
err = cr.InsertGlobalData(cfg, *db) err = cr.InsertGlobalData(cfg, *db)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
// Processes data for 'Pays' // Processes data for 'Pays'
err = cr.InsertPaysData(cfg, *db) err = cr.InsertPaysData(cfg, *db)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
} }

23
go.mod
View File

@ -1,11 +1,22 @@
module git.paulbsd.com/paulbsd/coronafana module git.paulbsd.com/paulbsd/coronafana
go 1.14 go 1.21
require ( require (
github.com/go-sql-driver/mysql v1.5.0 github.com/cretz/bine v0.2.0
github.com/jinzhu/gorm v1.9.12 github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jmoiron/sqlx v1.2.0 github.com/denisenkom/go-mssqldb v0.12.3 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect github.com/go-sql-driver/mysql v1.7.1
gopkg.in/ini.v1 v1.54.0 github.com/jinzhu/gorm v1.9.16
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.7 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1 // indirect
) )
require github.com/jinzhu/inflection v1.0.0 // indirect

117
go.sum
View File

@ -1,48 +1,103 @@
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
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=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
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/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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
gopkg.in/ini.v1 v1.54.0 h1:oM5ElzbIi7gwLnNbPX2M25ED1vSAK3B6dex50eS/6Fs= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/ini.v1 v1.54.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=

View File

@ -3,20 +3,21 @@ package coronafana
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"git.paulbsd.com/paulbsd/coronafana/src/config" "git.paulbsd.com/paulbsd/coronafana/src/config"
"github.com/cretz/bine/tor"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
// GetData fetch data from open data portal // GetData fetch data from open data portal
func GetData(cfg config.Config) (cr Coronafana, err error) { func GetData(cfg config.Config, d *tor.Dialer) (cr Coronafana, err error) {
var client http.Client var client = &http.Client{Transport: &http.Transport{DialContext: d.DialContext}}
log.Println("Getting JSON file ...") log.Println("Getting JSON file ...")
@ -30,7 +31,7 @@ func GetData(cfg config.Config) (cr Coronafana, err error) {
return return
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return return
} }
@ -71,7 +72,7 @@ func (cr Coronafana) InsertGlobalData(cfg config.Config, db sqlx.DB) (err error)
} }
if i > 0 { if i > 0 {
log.Println(fmt.Sprintf("%d global entries to inserted", i)) log.Println(fmt.Sprintf("%d global entries to insert", i))
} else { } else {
log.Println("No entry inserted") log.Println("No entry inserted")
} }
@ -106,7 +107,7 @@ func (cr Coronafana) InsertPaysData(cfg config.Config, db sqlx.DB) (err error) {
} }
if i > 0 { if i > 0 {
log.Println(fmt.Sprintf("%d global entries to inserted", i)) log.Println(fmt.Sprintf("%d global entries to insert", i))
} else { } else {
log.Println("No entry inserted") log.Println("No entry inserted")
} }

25
src/network/main.go Normal file
View File

@ -0,0 +1,25 @@
package network
import (
"context"
"log"
"time"
"github.com/cretz/bine/tor"
)
// InitTorSession initialize a tor session
func InitTorSession() (t *tor.Tor, dialer *tor.Dialer, dialCancel context.CancelFunc, err error) {
t, err = tor.Start(nil, nil)
if err != nil {
log.Panicf("Failed to start tor: %v", err)
}
// Wait at most a minute to start network and get
dialCtx, dialCancel := context.WithTimeout(context.Background(), time.Minute)
dialer, err = t.Dialer(dialCtx, nil)
if err != nil {
return
}
return
}

21
vendor/github.com/cretz/bine/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Chad Retz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,108 @@
package control
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"io/ioutil"
"strings"
)
// Authenticate authenticates with the Tor instance using the "best" possible
// authentication method if not already authenticated and sets the Authenticated
// field. The password argument is optional, and will only be used if the
// "SAFECOOKIE" and "NULL" authentication methods are not available and
// "HASHEDPASSWORD" is.
func (c *Conn) Authenticate(password string) error {
if c.Authenticated {
return nil
}
// Determine the supported authentication methods, and the cookie path.
pi, err := c.ProtocolInfo()
if err != nil {
return err
}
// Get the bytes to pass to with authenticate
var authBytes []byte
if pi.HasAuthMethod("NULL") {
// No auth bytes
} else if pi.HasAuthMethod("SAFECOOKIE") {
if pi.CookieFile == "" {
return c.protoErr("Invalid (empty) COOKIEFILE")
}
cookie, err := ioutil.ReadFile(pi.CookieFile)
if err != nil {
return c.protoErr("Failed to read COOKIEFILE: %v", err)
} else if len(cookie) != 32 {
return c.protoErr("Invalid cookie file length: %v", len(cookie))
}
// Send an AUTHCHALLENGE command, and parse the response.
var clientNonce [32]byte
if _, err := rand.Read(clientNonce[:]); err != nil {
return c.protoErr("Failed to generate clientNonce: %v", err)
}
resp, err := c.SendRequest("AUTHCHALLENGE %s %s", "SAFECOOKIE", hex.EncodeToString(clientNonce[:]))
if err != nil {
return err
}
splitResp := strings.Split(resp.Reply, " ")
if len(splitResp) != 3 || !strings.HasPrefix(splitResp[1], "SERVERHASH=") ||
!strings.HasPrefix(splitResp[2], "SERVERNONCE=") {
return c.protoErr("Invalid AUTHCHALLENGE response")
}
serverHash, err := hex.DecodeString(splitResp[1][11:])
if err != nil {
return c.protoErr("Failed to decode ServerHash: %v", err)
}
if len(serverHash) != 32 {
return c.protoErr("Invalid ServerHash length: %d", len(serverHash))
}
serverNonce, err := hex.DecodeString(splitResp[2][12:])
if err != nil {
return c.protoErr("Failed to decode ServerNonce: %v", err)
}
if len(serverNonce) != 32 {
return c.protoErr("Invalid ServerNonce length: %d", len(serverNonce))
}
// Validate the ServerHash.
m := hmac.New(sha256.New, []byte("Tor safe cookie authentication server-to-controller hash"))
m.Write(cookie)
m.Write(clientNonce[:])
m.Write(serverNonce)
dervServerHash := m.Sum(nil)
if !hmac.Equal(serverHash, dervServerHash) {
return c.protoErr("invalid ServerHash: mismatch")
}
// Calculate the ClientHash, and issue the AUTHENTICATE.
m = hmac.New(sha256.New, []byte("Tor safe cookie authentication controller-to-server hash"))
m.Write(cookie)
m.Write(clientNonce[:])
m.Write(serverNonce)
authBytes = m.Sum(nil)
} else if pi.HasAuthMethod("HASHEDPASSWORD") {
// Despite the name HASHEDPASSWORD, the raw password is actually sent. According to the code, this can either be
// a QuotedString, or base16 encoded, so go with the later since it's easier to handle.
if password == "" {
return c.protoErr("password auth needs a password")
}
authBytes = []byte(password)
} else {
return c.protoErr("No supported authentication methods")
}
// Send it
if err = c.sendAuthenticate(authBytes); err == nil {
c.Authenticated = true
}
return err
}
func (c *Conn) sendAuthenticate(byts []byte) error {
if len(byts) == 0 {
return c.sendRequestIgnoreResponse("AUTHENTICATE")
}
return c.sendRequestIgnoreResponse("AUTHENTICATE %v", hex.EncodeToString(byts))
}

38
vendor/github.com/cretz/bine/control/cmd_circuit.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
package control
import (
"strings"
)
// ExtendCircuit invokes EXTENDCIRCUIT and returns the circuit ID on success.
func (c *Conn) ExtendCircuit(circuitID string, path []string, purpose string) (string, error) {
if circuitID == "" {
circuitID = "0"
}
cmd := "EXTENDCIRCUIT " + circuitID
if len(path) > 0 {
cmd += " " + strings.Join(path, ",")
}
if purpose != "" {
cmd += " purpose=" + purpose
}
resp, err := c.SendRequest(cmd)
if err != nil {
return "", err
}
return resp.Reply[strings.LastIndexByte(resp.Reply, ' ')+1:], nil
}
// SetCircuitPurpose invokes SETCIRCUITPURPOSE.
func (c *Conn) SetCircuitPurpose(circuitID string, purpose string) error {
return c.sendRequestIgnoreResponse("SETCIRCUITPURPOSE %v purpose=%v", circuitID, purpose)
}
// CloseCircuit invokes CLOSECIRCUIT.
func (c *Conn) CloseCircuit(circuitID string, flags []string) error {
cmd := "CLOSECIRCUIT " + circuitID
for _, flag := range flags {
cmd += " " + flag
}
return c.sendRequestIgnoreResponse(cmd)
}

65
vendor/github.com/cretz/bine/control/cmd_conf.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
package control
import (
"strings"
"github.com/cretz/bine/torutil"
)
// SetConf invokes SETCONF.
func (c *Conn) SetConf(entries ...*KeyVal) error {
return c.sendSetConf("SETCONF", entries)
}
// ResetConf invokes RESETCONF.
func (c *Conn) ResetConf(entries ...*KeyVal) error {
return c.sendSetConf("RESETCONF", entries)
}
func (c *Conn) sendSetConf(cmd string, entries []*KeyVal) error {
for _, entry := range entries {
cmd += " " + entry.Key
if entry.ValSet() {
cmd += "=" + torutil.EscapeSimpleQuotedStringIfNeeded(entry.Val)
}
}
return c.sendRequestIgnoreResponse(cmd)
}
// GetConf invokes GETCONF and returns the values for the requested keys.
func (c *Conn) GetConf(keys ...string) ([]*KeyVal, error) {
resp, err := c.SendRequest("GETCONF %v", strings.Join(keys, " "))
if err != nil {
return nil, err
}
data := resp.DataWithReply()
ret := make([]*KeyVal, 0, len(data))
for _, data := range data {
key, val, ok := torutil.PartitionString(data, '=')
entry := &KeyVal{Key: key}
if ok {
if entry.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(val); err != nil {
return nil, err
}
if len(entry.Val) == 0 {
entry.ValSetAndEmpty = true
}
}
ret = append(ret, entry)
}
return ret, nil
}
// SaveConf invokes SAVECONF.
func (c *Conn) SaveConf(force bool) error {
cmd := "SAVECONF"
if force {
cmd += " FORCE"
}
return c.sendRequestIgnoreResponse(cmd)
}
// LoadConf invokes LOADCONF.
func (c *Conn) LoadConf(conf string) error {
return c.sendRequestIgnoreResponse("+LOADCONF\r\n%v\r\n.", conf)
}

1218
vendor/github.com/cretz/bine/control/cmd_event.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
package control
// GetHiddenServiceDescriptorAsync invokes HSFETCH.
func (c *Conn) GetHiddenServiceDescriptorAsync(address string, server string) error {
cmd := "HSFETCH " + address
if server != "" {
cmd += " SERVER=" + server
}
return c.sendRequestIgnoreResponse(cmd)
}
// PostHiddenServiceDescriptorAsync invokes HSPOST.
func (c *Conn) PostHiddenServiceDescriptorAsync(desc string, servers []string, address string) error {
cmd := "+HSPOST"
for _, server := range servers {
cmd += " SERVER=" + server
}
if address != "" {
cmd += "HSADDRESS=" + address
}
cmd += "\r\n" + desc + "\r\n."
return c.sendRequestIgnoreResponse(cmd)
}

92
vendor/github.com/cretz/bine/control/cmd_misc.go generated vendored Normal file
View File

@ -0,0 +1,92 @@
package control
import (
"strings"
"github.com/cretz/bine/torutil"
)
// Signal invokes SIGNAL.
func (c *Conn) Signal(signal string) error {
return c.sendRequestIgnoreResponse("SIGNAL %v", signal)
}
// Quit invokes QUIT.
func (c *Conn) Quit() error {
return c.sendRequestIgnoreResponse("QUIT")
}
// MapAddresses invokes MAPADDRESS and returns mapped addresses.
func (c *Conn) MapAddresses(addresses ...*KeyVal) ([]*KeyVal, error) {
cmd := "MAPADDRESS"
for _, address := range addresses {
cmd += " " + address.Key + "=" + address.Val
}
resp, err := c.SendRequest(cmd)
if err != nil {
return nil, err
}
data := resp.DataWithReply()
ret := make([]*KeyVal, 0, len(data))
for _, address := range data {
mappedAddress := &KeyVal{}
mappedAddress.Key, mappedAddress.Val, _ = torutil.PartitionString(address, '=')
ret = append(ret, mappedAddress)
}
return ret, nil
}
// GetInfo invokes GETINTO and returns values for requested keys.
func (c *Conn) GetInfo(keys ...string) ([]*KeyVal, error) {
resp, err := c.SendRequest("GETINFO %v", strings.Join(keys, " "))
if err != nil {
return nil, err
}
ret := make([]*KeyVal, 0, len(resp.Data))
for _, val := range resp.Data {
infoVal := &KeyVal{}
infoVal.Key, infoVal.Val, _ = torutil.PartitionString(val, '=')
if infoVal.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(infoVal.Val); err != nil {
return nil, err
}
ret = append(ret, infoVal)
}
return ret, nil
}
// PostDescriptor invokes POSTDESCRIPTOR.
func (c *Conn) PostDescriptor(descriptor string, purpose string, cache string) error {
cmd := "+POSTDESCRIPTOR"
if purpose != "" {
cmd += " purpose=" + purpose
}
if cache != "" {
cmd += " cache=" + cache
}
cmd += "\r\n" + descriptor + "\r\n."
return c.sendRequestIgnoreResponse(cmd)
}
// UseFeatures invokes USEFEATURE.
func (c *Conn) UseFeatures(features ...string) error {
return c.sendRequestIgnoreResponse("USEFEATURE " + strings.Join(features, " "))
}
// ResolveAsync invokes RESOLVE.
func (c *Conn) ResolveAsync(address string, reverse bool) error {
cmd := "RESOLVE "
if reverse {
cmd += "mode=reverse "
}
return c.sendRequestIgnoreResponse(cmd + address)
}
// TakeOwnership invokes TAKEOWNERSHIP.
func (c *Conn) TakeOwnership() error {
return c.sendRequestIgnoreResponse("TAKEOWNERSHIP")
}
// DropGuards invokes DROPGUARDS.
func (c *Conn) DropGuards() error {
return c.sendRequestIgnoreResponse("DROPGUARDS")
}

201
vendor/github.com/cretz/bine/control/cmd_onion.go generated vendored Normal file
View File

@ -0,0 +1,201 @@
package control
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"fmt"
"strconv"
"strings"
"github.com/cretz/bine/torutil"
"github.com/cretz/bine/torutil/ed25519"
)
// KeyType is a key type for Key in AddOnion.
type KeyType string
const (
// KeyTypeNew is NEW.
KeyTypeNew KeyType = "NEW"
// KeyTypeRSA1024 is RSA1024.
KeyTypeRSA1024 KeyType = "RSA1024"
// KeyTypeED25519V3 is ED25519-V3.
KeyTypeED25519V3 KeyType = "ED25519-V3"
)
// KeyAlgo is a key algorithm for GenKey on AddOnion.
type KeyAlgo string
const (
// KeyAlgoBest is BEST.
KeyAlgoBest KeyAlgo = "BEST"
// KeyAlgoRSA1024 is RSA1024.
KeyAlgoRSA1024 KeyAlgo = "RSA1024"
// KeyAlgoED25519V3 is ED25519-V3.
KeyAlgoED25519V3 KeyAlgo = "ED25519-V3"
)
// Key is a type of key to use for AddOnion. Implementations include GenKey,
// RSAKey, and ED25519Key.
type Key interface {
// Type is the KeyType for AddOnion.
Type() KeyType
// Blob is the serialized key for AddOnion.
Blob() string
}
// KeyFromString creates a Key for AddOnion based on a response string.
func KeyFromString(str string) (Key, error) {
typ, blob, _ := torutil.PartitionString(str, ':')
switch KeyType(typ) {
case KeyTypeNew:
return GenKeyFromBlob(blob), nil
case KeyTypeRSA1024:
return RSA1024KeyFromBlob(blob)
case KeyTypeED25519V3:
return ED25519KeyFromBlob(blob)
default:
return nil, fmt.Errorf("Unrecognized key type: %v", typ)
}
}
// GenKey is a Key for AddOnion that asks Tor to generate a key for the given
// algorithm.
type GenKey KeyAlgo
// GenKeyFromBlob creates a GenKey for the given response blob which is a
// KeyAlgo.
func GenKeyFromBlob(blob string) GenKey { return GenKey(KeyAlgo(blob)) }
// Type implements Key.Type.
func (GenKey) Type() KeyType { return KeyTypeNew }
// Blob implements Key.Blob.
func (g GenKey) Blob() string { return string(g) }
// RSAKey is a Key for AddOnion that is a RSA-1024 key (i.e. v2).
type RSAKey struct{ *rsa.PrivateKey }
// RSA1024KeyFromBlob creates a RSAKey for the given response blob.
func RSA1024KeyFromBlob(blob string) (*RSAKey, error) {
byts, err := base64.StdEncoding.DecodeString(blob)
if err != nil {
return nil, err
}
rsaKey, err := x509.ParsePKCS1PrivateKey(byts)
if err != nil {
return nil, err
}
return &RSAKey{rsaKey}, nil
}
// Type implements Key.Type.
func (*RSAKey) Type() KeyType { return KeyTypeRSA1024 }
// Blob implements Key.Blob.
func (r *RSAKey) Blob() string {
return base64.StdEncoding.EncodeToString(x509.MarshalPKCS1PrivateKey(r.PrivateKey))
}
// ED25519Key is a Key for AddOnion that is a ed25519 key (i.e. v3).
type ED25519Key struct{ ed25519.KeyPair }
// ED25519KeyFromBlob creates a ED25519Key for the given response blob.
func ED25519KeyFromBlob(blob string) (*ED25519Key, error) {
byts, err := base64.StdEncoding.DecodeString(blob)
if err != nil {
return nil, err
}
return &ED25519Key{ed25519.PrivateKey(byts).KeyPair()}, nil
}
// Type implements Key.Type.
func (*ED25519Key) Type() KeyType { return KeyTypeED25519V3 }
// Blob implements Key.Blob.
func (e *ED25519Key) Blob() string { return base64.StdEncoding.EncodeToString(e.PrivateKey()) }
// AddOnionRequest is a set of request params for AddOnion.
type AddOnionRequest struct {
// Key is the key to use or GenKey if Tor should generate it.
Key Key
// Flags are ADD_ONION flags.
Flags []string
// MaxStreams is ADD_ONION MaxStreams.
MaxStreams int
// Ports are ADD_ONION Port values. Key is virtual port, Val is target
// port (or can be empty to use virtual port).
Ports []*KeyVal
// ClientAuths are ADD_ONION ClientAuth values. If value is empty string,
// Tor will generate the password.
ClientAuths map[string]string
}
// AddOnionResponse is the response for AddOnion.
type AddOnionResponse struct {
// ServiceID is the ADD_ONION response ServiceID value.
ServiceID string
// Key is the ADD_ONION response PrivateKey value.
Key Key
// ClientAuths are the ADD_ONION response ClientAuth values.
ClientAuths map[string]string
// RawResponse is the raw ADD_ONION response.
RawResponse *Response
}
// AddOnion invokes ADD_ONION and returns its response.
func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
// Build command
if req.Key == nil {
return nil, c.protoErr("Key required")
}
cmd := "ADD_ONION " + string(req.Key.Type()) + ":" + req.Key.Blob()
if len(req.Flags) > 0 {
cmd += " Flags=" + strings.Join(req.Flags, ",")
}
if req.MaxStreams > 0 {
cmd += " MaxStreams=" + strconv.Itoa(req.MaxStreams)
}
for _, port := range req.Ports {
cmd += " Port=" + port.Key
if port.Val != "" {
cmd += "," + port.Val
}
}
for name, blob := range req.ClientAuths {
cmd += " ClientAuth=" + name
if blob != "" {
cmd += ":" + blob
}
}
// Invoke and read response
resp, err := c.SendRequest(cmd)
if err != nil {
return nil, err
}
ret := &AddOnionResponse{RawResponse: resp}
for _, data := range resp.Data {
key, val, _ := torutil.PartitionString(data, '=')
switch key {
case "ServiceID":
ret.ServiceID = val
case "PrivateKey":
if ret.Key, err = KeyFromString(val); err != nil {
return nil, err
}
case "ClientAuth":
name, pass, _ := torutil.PartitionString(val, ':')
if ret.ClientAuths == nil {
ret.ClientAuths = map[string]string{}
}
ret.ClientAuths[name] = pass
}
}
return ret, nil
}
// DelOnion invokes DELONION.
func (c *Conn) DelOnion(serviceID string) error {
return c.sendRequestIgnoreResponse("DEL_ONION %v", serviceID)
}

View File

@ -0,0 +1,76 @@
package control
import (
"strings"
"github.com/cretz/bine/torutil"
)
// ProtocolInfo is the protocol info result of Conn.ProtocolInfo.
type ProtocolInfo struct {
AuthMethods []string
CookieFile string
TorVersion string
RawResponse *Response
}
// HasAuthMethod checks if ProtocolInfo contains the requested auth method.
func (p *ProtocolInfo) HasAuthMethod(authMethod string) bool {
for _, m := range p.AuthMethods {
if m == authMethod {
return true
}
}
return false
}
// ProtocolInfo invokes PROTOCOLINFO on first invocation and returns a cached
// result on all others.
func (c *Conn) ProtocolInfo() (*ProtocolInfo, error) {
var err error
if c.protocolInfo == nil {
c.protocolInfo, err = c.sendProtocolInfo()
}
return c.protocolInfo, err
}
func (c *Conn) sendProtocolInfo() (*ProtocolInfo, error) {
resp, err := c.SendRequest("PROTOCOLINFO")
if err != nil {
return nil, err
}
// Check data vals
ret := &ProtocolInfo{RawResponse: resp}
for _, piece := range resp.Data {
key, val, ok := torutil.PartitionString(piece, ' ')
if !ok {
continue
}
switch key {
case "PROTOCOLINFO":
if val != "1" {
return nil, c.protoErr("Invalid PIVERSION: %v", val)
}
case "AUTH":
methods, cookieFile, _ := torutil.PartitionString(val, ' ')
if !strings.HasPrefix(methods, "METHODS=") {
continue
}
if cookieFile != "" {
if !strings.HasPrefix(cookieFile, "COOKIEFILE=") {
continue
}
if ret.CookieFile, err = torutil.UnescapeSimpleQuotedString(cookieFile[11:]); err != nil {
continue
}
}
ret.AuthMethods = strings.Split(methods[8:], ",")
case "VERSION":
torVersion, _, _ := torutil.PartitionString(val, ' ')
if strings.HasPrefix(torVersion, "Tor=") {
ret.TorVersion, err = torutil.UnescapeSimpleQuotedString(torVersion[4:])
}
}
}
return ret, nil
}

31
vendor/github.com/cretz/bine/control/cmd_stream.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
package control
import (
"strconv"
)
// AttachStream invokes ATTACHSTREAM.
func (c *Conn) AttachStream(streamID string, circuitID string, hopNum int) error {
if circuitID == "" {
circuitID = "0"
}
cmd := "ATTACHSTREAM " + streamID + " " + circuitID
if hopNum > 0 {
cmd += " HOP=" + strconv.Itoa(hopNum)
}
return c.sendRequestIgnoreResponse(cmd)
}
// RedirectStream invokes REDIRECTSTREAM.
func (c *Conn) RedirectStream(streamID string, address string, port int) error {
cmd := "REDIRECTSTREAM " + streamID + " " + address
if port > 0 {
cmd += " " + strconv.Itoa(port)
}
return c.sendRequestIgnoreResponse(cmd)
}
// CloseStream invokes CLOSESTREAM.
func (c *Conn) CloseStream(streamID string, reason string) error {
return c.sendRequestIgnoreResponse("CLOSESTREAM %v %v", streamID, reason)
}

102
vendor/github.com/cretz/bine/control/conn.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
package control
import (
"fmt"
"io"
"net/textproto"
"sync"
)
// Conn is the connection to the Tor control port.
type Conn struct {
// DebugWriter is the writer that debug logs for this library (not Tor
// itself) will be written to. If nil, no debug logs are generated/written.
DebugWriter io.Writer
// This is the underlying connection.
conn *textproto.Conn
// This is set lazily by ProtocolInfo().
protocolInfo *ProtocolInfo
// True if Authenticate has been called successfully.
Authenticated bool
// The lock fot eventListeners
eventListenersLock sync.RWMutex
// The value slices can be traversed outside of lock, they are completely
// replaced on change, never mutated. But the map itself must be locked on
// when reading or writing.
eventListeners map[EventCode][]chan<- Event
// This mutex is locked on when an entire response needs to be read. It
// helps synchronize accesses to the response by the asynchronous response
// listeners and the synchronous responses.
readLock sync.Mutex
}
// NewConn creates a Conn from the given textproto connection.
func NewConn(conn *textproto.Conn) *Conn {
return &Conn{
conn: conn,
eventListeners: map[EventCode][]chan<- Event{},
}
}
func (c *Conn) sendRequestIgnoreResponse(format string, args ...interface{}) error {
_, err := c.SendRequest(format, args...)
return err
}
// SendRequest sends a synchronous request to Tor and awaits the response. If
// the response errors, the error result will be set, but the response will be
// set also. This is usually not directly used by callers, but instead called by
// higher-level methods.
func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) {
if c.debugEnabled() {
c.debugf("Write line: %v", fmt.Sprintf(format, args...))
}
id, err := c.conn.Cmd(format, args...)
if err != nil {
return nil, err
}
c.readLock.Lock()
defer c.readLock.Unlock()
c.conn.StartResponse(id)
defer c.conn.EndResponse(id)
// Get the first non-async response
var resp *Response
for {
if resp, err = c.ReadResponse(); err != nil || !resp.IsAsync() {
break
}
c.relayAsyncEvents(resp)
}
if err == nil && !resp.IsOk() {
err = resp.Err
}
return resp, err
}
// Close sends a QUIT and closes the underlying Tor connection. This does not
// error if the QUIT is not accepted but does relay any error that occurs while
// closing the underlying connection.
func (c *Conn) Close() error {
// Ignore the response and ignore the error
c.Quit()
return c.conn.Close()
}
func (c *Conn) debugEnabled() bool {
return c.DebugWriter != nil
}
func (c *Conn) debugf(format string, args ...interface{}) {
if w := c.DebugWriter; w != nil {
fmt.Fprintf(w, format+"\n", args...)
}
}
func (*Conn) protoErr(format string, args ...interface{}) textproto.ProtocolError {
return textproto.ProtocolError(fmt.Sprintf(format, args...))
}

10
vendor/github.com/cretz/bine/control/doc.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
// Package control implements a low-level client for the Tor control spec
// version 1.
//
// The primary entrypoint is the Conn struct, instantiated with NewConn. This is
// the low-level layer to the control port of an already-running Tor instance.
// Most developers will prefer the tor package adjacent to this one for a higher
// level abstraction over the process and this connection.
//
// Some of this code is lifted from https://github.com/yawning/bulb with thanks.
package control

40
vendor/github.com/cretz/bine/control/keyval.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
package control
// KeyVal is a simple key-value struct. In cases where Val can be nil, an empty
// string represents that unless ValSetAndEmpty is true.
type KeyVal struct {
// Key is the always-present key
Key string
// Val is the value. If it's an empty string and nils are accepted/supported
// where this is used, it means nil unless ValSetAndEmpty is true.
Val string
// ValSetAndEmpty is true when Val is an empty string, the associated
// command supports nils, and Val should NOT be treated as nil. False
// otherwise.
ValSetAndEmpty bool
}
// NewKeyVal creates a new key-value pair.
func NewKeyVal(key string, val string) *KeyVal {
return &KeyVal{Key: key, Val: val}
}
// KeyVals creates multiple new key-value pairs from the given strings. The
// provided set of strings must have a length that is a multiple of 2.
func KeyVals(keysAndVals ...string) []*KeyVal {
if len(keysAndVals)%2 != 0 {
panic("Expected multiple of 2")
}
ret := make([]*KeyVal, len(keysAndVals)/2)
for i := 0; i < len(ret); i++ {
ret[i] = NewKeyVal(keysAndVals[i*2], keysAndVals[i*2+1])
}
return ret
}
// ValSet returns true if Val is either non empty or ValSetAndEmpty is true.
func (k *KeyVal) ValSet() bool {
return len(k.Val) > 0 || k.ValSetAndEmpty
}

106
vendor/github.com/cretz/bine/control/response.go generated vendored Normal file
View File

@ -0,0 +1,106 @@
package control
import (
"net/textproto"
"strconv"
"strings"
)
// Response is a response to a control port command or an asynchronous event.
type Response struct {
// Err is the status code and string representation associated with a
// response. Responses that have completed successfully will also have Err
// set to indicate such.
Err *textproto.Error
// Reply is the text on the EndReplyLine of the response.
Reply string
// Data is the MidReplyLines/DataReplyLines of the response. Dot encoded
// data is "decoded" and presented as a single string (terminal ".CRLF"
// removed, all intervening CRs stripped).
Data []string
// RawLines is all of the lines of a response, without CRLFs.
RawLines []string
}
// IsOk returns true if the response status code indicates success or an
// asynchronous event.
func (r *Response) IsOk() bool {
switch r.Err.Code {
case StatusOk, StatusOkUnnecessary, StatusAsyncEvent:
return true
default:
return false
}
}
// DataWithReply returns a combination of Data and Reply to give a full set of
// the lines of the response.
func (r *Response) DataWithReply() []string {
ret := make([]string, len(r.Data)+1)
copy(ret, r.Data)
ret[len(ret)-1] = r.Reply
return ret
}
// IsAsync returns true if the response is an asynchronous event.
func (r *Response) IsAsync() bool {
return r.Err.Code == StatusAsyncEvent
}
// ReadResponse returns the next response object.
func (c *Conn) ReadResponse() (*Response, error) {
var resp *Response
var statusCode int
for {
line, err := c.conn.ReadLine()
if err != nil {
return nil, err
}
c.debugf("Read line: %v", line)
// Parse the line that was just read.
if len(line) < 4 {
return nil, c.protoErr("Truncated response: %v", line)
}
if code, err := strconv.Atoi(line[0:3]); err != nil || code < 100 {
return nil, c.protoErr("Invalid status code: %v", line[0:3])
} else if resp == nil {
resp = &Response{}
statusCode = code
} else if code != statusCode {
// The status code should stay fixed for all lines of the response, since events can't be interleaved with
// response lines.
return nil, c.protoErr("Status code changed: %v != %v", code, statusCode)
}
resp.RawLines = append(resp.RawLines, line)
switch line[3] {
case ' ':
// Final line in the response.
resp.Reply = line[4:]
resp.Err = statusCodeToError(statusCode, resp.Reply)
return resp, nil
case '-':
// Continuation, keep reading.
resp.Data = append(resp.Data, line[4:])
case '+':
// A "dot-encoded" payload follows.
dotBody, err := c.conn.ReadDotBytes()
if err != nil {
return nil, err
}
dotBodyStr := strings.TrimRight(string(dotBody), "\n\r")
// c.debugf("Read dot body:\n---\n%v\n---", dotBodyStr)
resp.Data = append(resp.Data, line[4:]+"\r\n"+dotBodyStr)
dotLines := strings.Split(dotBodyStr, "\n")
for _, dotLine := range dotLines[:len(dotLines)-1] {
resp.RawLines = append(resp.RawLines, dotLine)
}
resp.RawLines = append(resp.RawLines, ".")
default:
return nil, c.protoErr("Invalid separator: '%v'", line[3])
}
}
}

64
vendor/github.com/cretz/bine/control/status.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
package control
import (
"fmt"
"net/textproto"
"strings"
)
// The various control port StatusCode constants.
const (
StatusOk = 250
StatusOkUnnecessary = 251
StatusErrResourceExhausted = 451
StatusErrSyntaxError = 500
StatusErrUnrecognizedCmd = 510
StatusErrUnimplementedCmd = 511
StatusErrSyntaxErrorArg = 512
StatusErrUnrecognizedCmdArg = 513
StatusErrAuthenticationRequired = 514
StatusErrBadAuthentication = 515
StatusErrUnspecifiedTorError = 550
StatusErrInternalError = 551
StatusErrUnrecognizedEntity = 552
StatusErrInvalidConfigValue = 553
StatusErrInvalidDescriptor = 554
StatusErrUnmanagedEntity = 555
StatusAsyncEvent = 650
)
var statusCodeStringMap = map[int]string{
StatusOk: "OK",
StatusOkUnnecessary: "Operation was unnecessary",
StatusErrResourceExhausted: "Resource exhausted",
StatusErrSyntaxError: "Syntax error: protocol",
StatusErrUnrecognizedCmd: "Unrecognized command",
StatusErrUnimplementedCmd: "Unimplemented command",
StatusErrSyntaxErrorArg: "Syntax error in command argument",
StatusErrUnrecognizedCmdArg: "Unrecognized command argument",
StatusErrAuthenticationRequired: "Authentication required",
StatusErrBadAuthentication: "Bad authentication",
StatusErrUnspecifiedTorError: "Unspecified Tor error",
StatusErrInternalError: "Internal error",
StatusErrUnrecognizedEntity: "Unrecognized entity",
StatusErrInvalidConfigValue: "Invalid configuration value",
StatusErrInvalidDescriptor: "Invalid descriptor",
StatusErrUnmanagedEntity: "Unmanaged entity",
StatusAsyncEvent: "Asynchronous event notification",
}
func statusCodeToError(code int, reply string) *textproto.Error {
err := new(textproto.Error)
err.Code = code
if msg, ok := statusCodeStringMap[code]; ok {
trimmedReply := strings.TrimSpace(strings.TrimPrefix(reply, msg))
err.Msg = fmt.Sprintf("%s: %s", msg, trimmedReply)
} else {
err.Msg = fmt.Sprintf("Unknown status code (%03d): %s", code, reply)
}
return err
}

82
vendor/github.com/cretz/bine/process/process.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
// Package process is the low-level abstraction for a Tor instance.
//
// The standard use is to create a Creator with NewCreator and the path to the
// Tor executable. The child package 'embedded' can be used if Tor is statically
// linked in the binary. Most developers will prefer the tor package adjacent to
// this one for a higher level abstraction over the process and control port
// connection.
package process
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"strconv"
"strings"
"github.com/cretz/bine/torutil"
)
// Process is the interface implemented by Tor processes.
type Process interface {
// Start starts the Tor process in the background and returns. It is
// analagous to os/exec.Cmd.Start.
Start() error
// Wait waits for the Tor process to exit and returns error if it was not a
// successful exit. It is analagous to os/exec.Cmd.Wait.
Wait() error
// ControlConn is used for statically linked, embedded processes to create
// a controller connection. For non-embedded processes or Tor versions that
// don't support embedded control connections, ErrControlConnUnsupported is
// returned. Note, this should only be called once per process before
// Start, and the connection does not need to be closed.
EmbeddedControlConn() (net.Conn, error)
}
// Creator is the interface for process creation.
type Creator interface {
New(ctx context.Context, args ...string) (Process, error)
}
type CmdCreatorFunc func(ctx context.Context, args ...string) (*exec.Cmd, error)
// NewCreator creates a Creator for external Tor process execution based on the
// given exe path.
func NewCreator(exePath string) Creator {
return CmdCreatorFunc(func(ctx context.Context, args ...string) (*exec.Cmd, error) {
cmd := exec.CommandContext(ctx, exePath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd, nil
})
}
type exeProcess struct {
*exec.Cmd
}
func (c CmdCreatorFunc) New(ctx context.Context, args ...string) (Process, error) {
cmd, err := c(ctx, args...)
return &exeProcess{cmd}, err
}
// ErrControlConnUnsupported is returned by Process.EmbeddedControlConn when
// it is unsupported.
var ErrControlConnUnsupported = fmt.Errorf("Control conn not supported")
func (e *exeProcess) EmbeddedControlConn() (net.Conn, error) {
return nil, ErrControlConnUnsupported
}
// ControlPortFromFileContents reads a control port file that is written by Tor
// when ControlPortWriteToFile is set.
func ControlPortFromFileContents(contents string) (int, error) {
contents = strings.TrimSpace(contents)
_, port, ok := torutil.PartitionString(contents, ':')
if !ok || !strings.HasPrefix(contents, "PORT=") {
return 0, fmt.Errorf("Invalid port format: %v", contents)
}
return strconv.Atoi(port)
}

111
vendor/github.com/cretz/bine/tor/dialer.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package tor
import (
"context"
"fmt"
"net"
"strings"
"golang.org/x/net/proxy"
)
// Dialer is a wrapper around a proxy.Dialer for dialing connections.
type Dialer struct {
proxy.Dialer
}
// DialConf is the configuration used for Dialer.
type DialConf struct {
// ProxyAddress is the address for the SOCKS5 proxy. If empty, it is looked
// up.
ProxyAddress string
// ProxyNetwork is the network for the SOCKS5 proxy. If ProxyAddress is
// empty, this value is ignored and overridden by what is looked up. If this
// is empty and ProxyAddress is not empty, it defaults to "tcp".
ProxyNetwork string
// ProxyAuth is the auth for the proxy. Since Tor's SOCKS5 proxy is
// unauthenticated, this is rarely needed. It can be used when
// IsolateSOCKSAuth is set to ensure separate circuits.
//
// This should not be confused with downstream SOCKS proxy authentication
// which is set via Tor values for Socks5ProxyUsername and
// Socks5ProxyPassword when Socks5Proxy is set.
ProxyAuth *proxy.Auth
// SkipEnableNetwork, if true, will skip the enable network step in Dialer.
SkipEnableNetwork bool
// Forward is the dialer to forward to. If nil, just uses normal net dialer.
Forward proxy.Dialer
}
// Dialer creates a new Dialer for the given configuration. Context can be nil.
// If conf is nil, a default is used.
func (t *Tor) Dialer(ctx context.Context, conf *DialConf) (*Dialer, error) {
if ctx == nil {
ctx = context.Background()
}
if conf == nil {
conf = &DialConf{}
}
// Enable the network if requested
if !conf.SkipEnableNetwork {
if err := t.EnableNetwork(ctx, true); err != nil {
return nil, err
}
}
// Lookup proxy address as needed
proxyNetwork := conf.ProxyNetwork
proxyAddress := conf.ProxyAddress
if proxyAddress == "" {
info, err := t.Control.GetInfo("net/listeners/socks")
if err != nil {
return nil, err
}
if len(info) != 1 || info[0].Key != "net/listeners/socks" {
return nil, fmt.Errorf("Unable to get socks proxy address")
}
proxyAddress = info[0].Val
if strings.HasPrefix(proxyAddress, "unix:") {
proxyAddress = proxyAddress[5:]
proxyNetwork = "unix"
} else {
proxyNetwork = "tcp"
}
} else if proxyNetwork == "" {
proxyNetwork = "tcp"
}
dialer, err := proxy.SOCKS5(proxyNetwork, proxyAddress, conf.ProxyAuth, conf.Forward)
if err != nil {
return nil, err
}
return &Dialer{dialer}, nil
}
// DialContext is the equivalent of net.DialContext.
//
// TODO: Remove when https://github.com/golang/go/issues/17759 is released.
func (d *Dialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
errCh := make(chan error, 1)
connCh := make(chan net.Conn, 1)
go func() {
if conn, err := d.Dial(network, addr); err != nil {
errCh <- err
} else if ctx.Err() != nil {
conn.Close()
} else {
connCh <- conn
}
}()
select {
case err := <-errCh:
return nil, err
case conn := <-connCh:
return conn, nil
case <-ctx.Done():
return nil, ctx.Err()
}
}

7
vendor/github.com/cretz/bine/tor/doc.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// Package tor is the high-level client for Tor.
//
// The Tor type is a combination of a Tor instance and a connection to it.
// Use Start to create Tor. Then Dialer or Listener can be used.
//
// Some of this code is lifted from https://github.com/yawning/bulb with thanks.
package tor

343
vendor/github.com/cretz/bine/tor/listen.go generated vendored Normal file
View File

@ -0,0 +1,343 @@
package tor
import (
"context"
"crypto"
"crypto/rsa"
"fmt"
"net"
"strconv"
"github.com/cretz/bine/control"
"github.com/cretz/bine/torutil/ed25519"
othered25519 "golang.org/x/crypto/ed25519"
)
// OnionService implements net.Listener and net.Addr for an onion service.
type OnionService struct {
// ID is the service ID for this onion service.
ID string
// Key is the private key for this service. It is either the set key, the
// generated key, or nil if asked to discard the key. If present, it is
// *crypto/rsa.PrivateKey (1024 bit) when Version3 is false or
// github.com/cretz/bine/torutil/ed25519.KeyPair when Version3 is true.
Key crypto.PrivateKey
// Version3 says whether or not this service is a V3 service.
Version3 bool
// ClientAuths is the credential set for clients. The keys are username and
// the values are credentials. The credentials will always be present even
// if Tor had to generate them.
ClientAuths map[string]string
// LocalListener is the local TCP listener. This is always present.
LocalListener net.Listener
// RemotePorts is the set of remote ports that are forwarded to the local
// listener. This will always have at least one value.
RemotePorts []int
// CloseLocalListenerOnClose is true if the local listener should be closed
// on Close. This is set to true if a listener was created by Listen and set
// to false of an existing LocalListener was provided to Listen.
CloseLocalListenerOnClose bool
// The Tor object that created this. Needed for Close.
Tor *Tor
}
// ListenConf is the configuration for Listen calls.
type ListenConf struct {
// LocalPort is the local port to create a TCP listener on. If the port is
// 0, it is automatically chosen. This is ignored if LocalListener is set.
LocalPort int
// LocalListener is the specific local listener to back the onion service.
// If this is nil (the default), then a listener is created with LocalPort.
LocalListener net.Listener
// RemotePorts are the remote ports to serve the onion service on. If empty,
// it is the same as the local port or local listener. This must have at
// least one value if the local listener is not a *net.TCPListener.
RemotePorts []int
// Key is the private key to use. If not present, a key is generated based
// on whether Version3 is true or false. If present, it must be a
// *crypto/rsa.PrivateKey (1024 bit), a
// github.com/cretz/bine/torutil/ed25519.KeyPair, a
// golang.org/x/crypto/ed25519.PrivateKey, or a
// github.com/cretz/bine/control.Key.
Key crypto.PrivateKey
// Version3 determines whether, when Key is nil, a version 2 or version 3
// service/key will be generated. If true it is version 3 (an ed25519 key
// and v3 onion service) and if false it is version 2 (a RSA-1024 key and v2
// onion service). If Key is not nil, this value is ignored.
Version3 bool
// ClientAuths is the set of usernames and credentials for client
// authentication. The keys are usernames and the values are credentials. If
// a username is present but the credential is empty, a credential is
// generated by Tor for that user. If this is empty there is no
// authentication.
ClientAuths map[string]string
// MaxStreams is the maximum number of streams the service will accept. 0
// means unlimited.
MaxStreams int
// DiscardKey, if true and Key is nil (meaning a private key is generated),
// tells Tor not to return the generated private key. This value is ignored
// if Key is not nil.
DiscardKey bool
// Detach, if true, prevents the default behavior of the onion service being
// deleted when this controller connection is closed.
Detach bool
// NonAnonymous must be true if Tor options HiddenServiceSingleHopMode and
// HiddenServiceNonAnonymousMode are set. Otherwise, it must be false.
NonAnonymous bool
// MaxStreamsCloseCircuit determines whether to close the circuit when the
// maximum number of streams is exceeded. If true, the circuit is closed. If
// false, the stream is simply not connected but the circuit stays open.
MaxStreamsCloseCircuit bool
// NoWait if true will not wait until the onion service is published. If
// false, the network will be enabled if it's not and then we will wait
// until the onion service is published.
NoWait bool
}
// Listen creates an onion service and local listener. The context can be nil.
// If conf is nil, the default struct value is used. Note, if this errors, any
// listeners created here are closed but if a LocalListener is provided it may remain open.
func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, error) {
if ctx == nil {
ctx = context.Background()
}
// Create the service up here and make sure we close it no matter the error within
svc := &OnionService{Tor: t, CloseLocalListenerOnClose: conf.LocalListener == nil}
var err error
// Create the local listener if necessary
svc.LocalListener = conf.LocalListener
if svc.LocalListener == nil {
if svc.LocalListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(conf.LocalPort)); err != nil {
return nil, err
}
}
// Henceforth, any error requires we close the svc
// Build the onion request
req := &control.AddOnionRequest{MaxStreams: conf.MaxStreams, ClientAuths: conf.ClientAuths}
// Set flags
if conf.DiscardKey {
req.Flags = append(req.Flags, "DiscardPK")
}
if conf.Detach {
req.Flags = append(req.Flags, "Detach")
}
if len(conf.ClientAuths) > 0 {
req.Flags = append(req.Flags, "BasicAuth")
}
if conf.NonAnonymous {
req.Flags = append(req.Flags, "NonAnonymous")
}
if conf.MaxStreamsCloseCircuit {
req.Flags = append(req.Flags, "MaxStreamsCloseCircuit")
}
// Set the key
switch key := conf.Key.(type) {
case nil:
svc.Version3 = conf.Version3
if conf.Version3 {
req.Key = control.GenKey(control.KeyAlgoED25519V3)
} else {
req.Key = control.GenKey(control.KeyAlgoRSA1024)
}
case control.GenKey:
svc.Version3 = conf.Version3
req.Key = key
case *rsa.PrivateKey:
svc.Key = key
svc.Version3 = false
if key.N == nil || key.N.BitLen() != 1024 {
err = fmt.Errorf("RSA key must be 1024 bits")
} else {
req.Key = &control.RSAKey{PrivateKey: key}
}
case *control.RSAKey:
svc.Key = key.PrivateKey
svc.Version3 = false
if key.N == nil || key.N.BitLen() != 1024 {
err = fmt.Errorf("RSA key must be 1024 bits")
} else {
req.Key = key
}
case ed25519.KeyPair:
svc.Key = key
svc.Version3 = true
req.Key = &control.ED25519Key{key}
case othered25519.PrivateKey:
properKey := ed25519.FromCryptoPrivateKey(key)
svc.Key = properKey
svc.Version3 = true
req.Key = &control.ED25519Key{properKey}
case *control.ED25519Key:
svc.Key = key.KeyPair
svc.Version3 = true
req.Key = key
default:
err = fmt.Errorf("Unrecognized key type: %T", key)
}
// Apply the remote ports
if err == nil {
if len(conf.RemotePorts) == 0 {
tcpAddr, ok := svc.LocalListener.Addr().(*net.TCPAddr)
if !ok {
err = fmt.Errorf("Unable to derive local TCP port")
} else {
svc.RemotePorts = []int{tcpAddr.Port}
}
} else {
svc.RemotePorts = make([]int, len(conf.RemotePorts))
copy(svc.RemotePorts, conf.RemotePorts)
}
}
// Apply the local ports with the remote ports
if err == nil {
localAddr := svc.LocalListener.Addr().String()
if _, ok := svc.LocalListener.(*net.UnixListener); ok {
localAddr = "unix:" + localAddr
}
for _, remotePort := range svc.RemotePorts {
req.Ports = append(req.Ports, &control.KeyVal{Key: strconv.Itoa(remotePort), Val: localAddr})
}
}
// Create the onion service
var resp *control.AddOnionResponse
if err == nil {
resp, err = t.Control.AddOnion(req)
}
// Apply the response to the service
if err == nil {
svc.ID = resp.ServiceID
switch key := resp.Key.(type) {
case nil:
// Do nothing
case *control.RSAKey:
svc.Key = key.PrivateKey
case *control.ED25519Key:
svc.Key = key.KeyPair
default:
err = fmt.Errorf("Unrecognized result key type: %T", key)
}
// Client auths are the conf and then overridden by results
if len(conf.ClientAuths) > 0 {
svc.ClientAuths = make(map[string]string, len(conf.ClientAuths))
for k, v := range conf.ClientAuths {
svc.ClientAuths[k] = v
}
for k, v := range resp.ClientAuths {
svc.ClientAuths[k] = v
}
}
}
// Wait if necessary
if err == nil && !conf.NoWait {
t.Debugf("Enabling network before waiting for publication")
// First make sure network is enabled
if err = t.EnableNetwork(ctx, true); err == nil {
t.Debugf("Waiting for publication")
// Now we'll take a similar approach to Stem. Several UPLOADs are sent out, so we count em. If we see
// UPLOADED, we succeeded. If we see failed, we count those. If there are as many failures as uploads, they
// all failed and it's a failure. NOTE: unlike Stem's comments that say they don't, we are actually seeing
// the service IDs for UPLOADED so we don't keep a map.
uploadsAttempted := 0
failures := []string{}
_, err = t.Control.EventWait(ctx, []control.EventCode{control.EventCodeHSDesc},
func(evt control.Event) (bool, error) {
hs, _ := evt.(*control.HSDescEvent)
if hs != nil && hs.Address == svc.ID {
switch hs.Action {
case "UPLOAD":
uploadsAttempted++
case "FAILED":
failures = append(failures,
fmt.Sprintf("Failed uploading to dir %v - reason: %v", hs.HSDir, hs.Reason))
if len(failures) == uploadsAttempted {
return false, fmt.Errorf("Failed all uploads, reasons: %v", failures)
}
case "UPLOADED":
return true, nil
}
}
return false, nil
})
}
}
// Give back err and close if there is an err
if err != nil {
if closeErr := svc.Close(); closeErr != nil {
err = fmt.Errorf("Error on listen: %v (also got error trying to close: %v)", err, closeErr)
}
return nil, err
}
return svc, nil
}
// Accept implements net.Listener.Accept.
func (o *OnionService) Accept() (net.Conn, error) {
return o.LocalListener.Accept()
}
// Addr implements net.Listener.Addr just returning this object.
func (o *OnionService) Addr() net.Addr {
return o
}
// Network implements net.Addr.Network always returning "tcp".
func (o *OnionService) Network() string {
return "tcp"
}
// String implements net.Addr.String and returns "<serviceID>.onion:<virtport>".
func (o *OnionService) String() string {
return fmt.Sprintf("%v.onion:%v", o.ID, o.RemotePorts[0])
}
// Close implements net.Listener.Close and deletes the onion service and closes
// the LocalListener if CloseLocalListenerOnClose is true.
func (o *OnionService) Close() (err error) {
o.Tor.Debugf("Closing onion %v", o)
// Delete the onion first
if o.ID != "" {
err = o.Tor.Control.DelOnion(o.ID)
o.ID = ""
}
// Now if the local one needs to be closed, do it
if o.CloseLocalListenerOnClose && o.LocalListener != nil {
if closeErr := o.LocalListener.Close(); closeErr != nil {
if err != nil {
err = fmt.Errorf("Unable to close onion: %v (also unable to close local listener: %v)", err, closeErr)
} else {
err = closeErr
}
}
o.LocalListener = nil
}
if err != nil {
o.Tor.Debugf("Failed closing onion: %v", err)
}
return
}

16
vendor/github.com/cretz/bine/tor/log.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
package tor
import "fmt"
// DebugEnabled returns true if there is a DebugWriter.
func (t *Tor) DebugEnabled() bool {
return t.DebugWriter != nil
}
// Debugf writes the formatted string with a newline appended to the DebugWriter
// if present.
func (t *Tor) Debugf(format string, args ...interface{}) {
if w := t.DebugWriter; w != nil {
fmt.Fprintf(w, format+"\n", args...)
}
}

453
vendor/github.com/cretz/bine/tor/tor.go generated vendored Normal file
View File

@ -0,0 +1,453 @@
package tor
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/textproto"
"os"
"path/filepath"
"strconv"
"time"
"github.com/cretz/bine/control"
"github.com/cretz/bine/process"
)
// Tor is the wrapper around the Tor process and control port connection. It
// should be created with Start and developers should always call Close when
// done.
type Tor struct {
// Process is the Tor instance that is running.
Process process.Process
// Control is the Tor controller connection.
Control *control.Conn
// ProcessCancelFunc is the context cancellation func for the Tor process.
// It is used by Close and should not be called directly. This can be nil.
ProcessCancelFunc context.CancelFunc
// ControlPort is the port that Control is connected on. It is 0 if the
// connection is an embedded control connection.
ControlPort int
// DataDir is the path to the data directory that Tor is using.
DataDir string
// DeleteDataDirOnClose is true if, when Close is invoked, the entire
// directory will be deleted.
DeleteDataDirOnClose bool
// DebugWriter is the writer used for debug logs, or nil if debug logs
// should not be emitted.
DebugWriter io.Writer
// StopProcessOnClose, if true, will attempt to halt the process on close.
StopProcessOnClose bool
// GeoIPCreatedFile is the path, relative to DataDir, that was created from
// StartConf.GeoIPFileReader. It is empty if no file was created.
GeoIPCreatedFile string
// GeoIPv6CreatedFile is the path, relative to DataDir, that was created
// from StartConf.GeoIPFileReader. It is empty if no file was created.
GeoIPv6CreatedFile string
}
// StartConf is the configuration used for Start when starting a Tor instance. A
// default instance with no fields set is the default used for Start.
type StartConf struct {
// ExePath is the path to the Tor executable. If it is not present, "tor" is
// used either locally or on the PATH. This is ignored if ProcessCreator is
// set.
ExePath string
// ProcessCreator is the override to use a specific process creator. If set,
// ExePath is ignored.
ProcessCreator process.Creator
// UseEmbeddedControlConn can be set to true to use
// process.Process.EmbeddedControlConn() instead of creating a connection
// via ControlPort. Note, this only works when ProcessCreator is an
// embedded Tor creator with version >= 0.3.5.x.
UseEmbeddedControlConn bool
// ControlPort is the port to use for the Tor controller. If it is 0, Tor
// picks a port for use. This is ignored if UseEmbeddedControlConn is true.
ControlPort int
// DataDir is the directory used by Tor. If it is empty, a temporary
// directory is created in TempDataDirBase.
DataDir string
// TempDataDirBase is the parent directory that a temporary data directory
// will be created under for use by Tor. This is ignored if DataDir is not
// empty. If empty it is assumed to be the current working directory.
TempDataDirBase string
// RetainTempDataDir, if true, will not set the created temporary data
// directory to be deleted on close. This is ignored if DataDir is not
// empty.
RetainTempDataDir bool
// DisableCookieAuth, if true, will not use the default SAFECOOKIE
// authentication mechanism for the Tor controller.
DisableCookieAuth bool
// DisableEagerAuth, if true, will not authenticate on Start.
DisableEagerAuth bool
// EnableNetwork, if true, will connect to the wider Tor network on start.
EnableNetwork bool
// ExtraArgs is the set of extra args passed to the Tor instance when
// started.
ExtraArgs []string
// TorrcFile is the torrc file to set on start. If empty, a blank torrc is
// created in the data directory and is used instead.
TorrcFile string
// DebugWriter is the writer to use for debug logs, or nil for no debug
// logs.
DebugWriter io.Writer
// NoHush if true does not set --hush. By default --hush is set.
NoHush bool
// NoAutoSocksPort if true does not set "--SocksPort auto" as is done by
// default. This means the caller could set their own or just let it
// default to 9050.
NoAutoSocksPort bool
// GeoIPReader, if present, is called before start to copy geo IP files to
// the data directory. Errors are propagated. If the ReadCloser is present,
// it is copied to the data dir, overwriting as necessary, and then closed
// and the appropriate command line argument is added to reference it. If
// both the ReadCloser and error are nil, no copy or command line argument
// is used for that version. This is called twice, once with false and once
// with true for ipv6.
//
// This can be set to torutil/geoipembed.GeoIPReader to use an embedded
// source.
GeoIPFileReader func(ipv6 bool) (io.ReadCloser, error)
}
// Start a Tor instance and connect to it. If ctx is nil, context.Background()
// is used. If conf is nil, a default instance is used.
func Start(ctx context.Context, conf *StartConf) (*Tor, error) {
if ctx == nil {
ctx = context.Background()
}
if conf == nil {
conf = &StartConf{}
}
tor := &Tor{DataDir: conf.DataDir, DebugWriter: conf.DebugWriter, StopProcessOnClose: true}
// Create the data dir and make it absolute
if tor.DataDir == "" {
tempBase := conf.TempDataDirBase
if tempBase == "" {
tempBase = "."
}
var err error
if tempBase, err = filepath.Abs(tempBase); err != nil {
return nil, err
}
if tor.DataDir, err = ioutil.TempDir(tempBase, "data-dir-"); err != nil {
return nil, fmt.Errorf("Unable to create temp data dir: %v", err)
}
tor.Debugf("Created temp data directory at: %v", tor.DataDir)
tor.DeleteDataDirOnClose = !conf.RetainTempDataDir
} else if err := os.MkdirAll(tor.DataDir, 0700); err != nil {
return nil, fmt.Errorf("Unable to create data dir: %v", err)
}
// !!!! From this point on, we must close tor if we error !!!!
// Copy geoip stuff if necessary
err := tor.copyGeoIPFiles(conf)
// Start tor
if err == nil {
err = tor.startProcess(ctx, conf)
}
// Connect the controller
if err == nil {
err = tor.connectController(ctx, conf)
}
// Attempt eager auth w/ no password
if err == nil && !conf.DisableEagerAuth {
err = tor.Control.Authenticate("")
}
// If there was an error, we have to try to close here but it may leave the process open
if err != nil {
if closeErr := tor.Close(); closeErr != nil {
err = fmt.Errorf("Error on start: %v (also got error trying to close: %v)", err, closeErr)
}
}
return tor, err
}
func (t *Tor) copyGeoIPFiles(conf *StartConf) error {
if conf.GeoIPFileReader == nil {
return nil
}
if r, err := conf.GeoIPFileReader(false); err != nil {
return fmt.Errorf("Unable to read geoip file: %v", err)
} else if r != nil {
t.GeoIPCreatedFile = "geoip"
if err := createFile(filepath.Join(t.DataDir, "geoip"), r); err != nil {
return fmt.Errorf("Unable to create geoip file: %v", err)
}
}
if r, err := conf.GeoIPFileReader(true); err != nil {
return fmt.Errorf("Unable to read geoip6 file: %v", err)
} else if r != nil {
t.GeoIPv6CreatedFile = "geoip6"
if err := createFile(filepath.Join(t.DataDir, "geoip6"), r); err != nil {
return fmt.Errorf("Unable to create geoip6 file: %v", err)
}
}
return nil
}
func createFile(to string, from io.ReadCloser) error {
f, err := os.Create(to)
if err == nil {
_, err = io.Copy(f, from)
if closeErr := f.Close(); err == nil {
err = closeErr
}
}
if closeErr := from.Close(); err == nil {
err = closeErr
}
return err
}
func (t *Tor) startProcess(ctx context.Context, conf *StartConf) error {
// Get the creator
creator := conf.ProcessCreator
if creator == nil {
torPath := conf.ExePath
if torPath == "" {
torPath = "tor"
}
creator = process.NewCreator(torPath)
}
// Build the args
args := []string{"--DataDirectory", t.DataDir}
if !conf.DisableCookieAuth {
args = append(args, "--CookieAuthentication", "1")
}
if !conf.EnableNetwork {
args = append(args, "--DisableNetwork", "1")
}
if !conf.NoHush {
args = append(args, "--hush")
}
if !conf.NoAutoSocksPort {
args = append(args, "--SocksPort", "auto")
}
if t.GeoIPCreatedFile != "" {
args = append(args, "--GeoIPFile", filepath.Join(t.DataDir, t.GeoIPCreatedFile))
}
if t.GeoIPv6CreatedFile != "" {
args = append(args, "--GeoIPv6File", filepath.Join(t.DataDir, t.GeoIPv6CreatedFile))
}
// If there is no Torrc file, create a blank temp one
torrcFileName := conf.TorrcFile
if torrcFileName == "" {
torrcFile, err := ioutil.TempFile(t.DataDir, "torrc-")
if err != nil {
return err
}
torrcFileName = torrcFile.Name()
if err = torrcFile.Close(); err != nil {
return err
}
}
args = append(args, "-f", torrcFileName)
// Create file for Tor to write the control port to if it's not told to us and we're not embedded
var controlPortFileName string
var err error
if !conf.UseEmbeddedControlConn {
if conf.ControlPort == 0 {
controlPortFile, err := ioutil.TempFile(t.DataDir, "control-port-")
if err != nil {
return err
}
controlPortFileName = controlPortFile.Name()
if err = controlPortFile.Close(); err != nil {
return err
}
args = append(args, "--ControlPort", "auto", "--ControlPortWriteToFile", controlPortFile.Name())
} else {
args = append(args, "--ControlPort", strconv.Itoa(conf.ControlPort))
}
}
// Create process creator with args
var processCtx context.Context
processCtx, t.ProcessCancelFunc = context.WithCancel(ctx)
args = append(args, conf.ExtraArgs...)
p, err := creator.New(processCtx, args...)
if err != nil {
return err
}
// Use the embedded conn if requested
if conf.UseEmbeddedControlConn {
t.Debugf("Using embedded control connection")
conn, err := p.EmbeddedControlConn()
if err != nil {
return fmt.Errorf("Unable to get embedded control conn: %v", err)
}
t.Control = control.NewConn(textproto.NewConn(conn))
t.Control.DebugWriter = t.DebugWriter
}
// Start process with the args
t.Debugf("Starting tor with args %v", args)
if err = p.Start(); err != nil {
return err
}
t.Process = p
// If not embedded, try a few times to read the control port file if we need to
if !conf.UseEmbeddedControlConn {
t.ControlPort = conf.ControlPort
if t.ControlPort == 0 {
ControlPortCheck:
for i := 0; i < 10; i++ {
select {
case <-ctx.Done():
err = ctx.Err()
break ControlPortCheck
default:
// Try to read the controlport file, or wait a bit
var byts []byte
if byts, err = ioutil.ReadFile(controlPortFileName); err != nil {
break ControlPortCheck
} else if t.ControlPort, err = process.ControlPortFromFileContents(string(byts)); err == nil {
break ControlPortCheck
}
time.Sleep(200 * time.Millisecond)
}
}
if err != nil {
return fmt.Errorf("Unable to read control port file: %v", err)
}
}
}
return nil
}
func (t *Tor) connectController(ctx context.Context, conf *StartConf) error {
// This doesn't apply if already connected (e.g. using embedded conn)
if t.Control != nil {
return nil
}
t.Debugf("Connecting to control port %v", t.ControlPort)
textConn, err := textproto.Dial("tcp", "127.0.0.1:"+strconv.Itoa(t.ControlPort))
if err != nil {
return err
}
t.Control = control.NewConn(textConn)
t.Control.DebugWriter = t.DebugWriter
return nil
}
// EnableNetwork sets DisableNetwork to 0 and optionally waits for bootstrap to
// complete. The context can be nil. If DisableNetwork isnt 1, this does
// nothing.
func (t *Tor) EnableNetwork(ctx context.Context, wait bool) error {
if ctx == nil {
ctx = context.Background()
}
// Only enable if DisableNetwork is 1
if vals, err := t.Control.GetConf("DisableNetwork"); err != nil {
return err
} else if len(vals) == 0 || vals[0].Key != "DisableNetwork" || vals[0].Val != "1" {
return nil
}
// Enable the network
if err := t.Control.SetConf(control.KeyVals("DisableNetwork", "0")...); err != nil {
return nil
}
// If not waiting, leave
if !wait {
return nil
}
// Wait for progress to hit 100
_, err := t.Control.EventWait(ctx, []control.EventCode{control.EventCodeStatusClient},
func(evt control.Event) (bool, error) {
if status, _ := evt.(*control.StatusEvent); status != nil && status.Action == "BOOTSTRAP" {
if status.Severity == "NOTICE" && status.Arguments["PROGRESS"] == "100" {
return true, nil
} else if status.Severity == "ERR" {
return false, fmt.Errorf("Failing bootstrapping, Tor warning: %v", status.Arguments["WARNING"])
}
}
return false, nil
})
return err
}
// Close sends a halt to the Tor process if it can, closes the controller
// connection, and stops the process.
func (t *Tor) Close() error {
t.Debugf("Closing Tor")
errs := []error{}
// If controller is authenticated, send the quit signal to the process. Otherwise, just close the controller.
sentHalt := false
if t.Control != nil {
if t.Control.Authenticated && t.StopProcessOnClose {
if err := t.Control.Signal("HALT"); err != nil {
errs = append(errs, fmt.Errorf("Unable to signal halt: %v", err))
} else {
sentHalt = true
}
}
// Now close the controller
if err := t.Control.Close(); err != nil {
errs = append(errs, fmt.Errorf("Unable to close contrlller: %v", err))
} else {
t.Control = nil
}
}
if t.Process != nil {
// If we didn't halt, we have to force kill w/ the cancel func
if !sentHalt && t.StopProcessOnClose {
t.ProcessCancelFunc()
}
// Wait for a bit to make sure it stopped
errCh := make(chan error, 1)
var waitErr error
go func() { errCh <- t.Process.Wait() }()
select {
case waitErr = <-errCh:
if waitErr != nil {
errs = append(errs, fmt.Errorf("Process wait failed: %v", waitErr))
}
case <-time.After(300 * time.Millisecond):
errs = append(errs, fmt.Errorf("Process did not exit after 300 ms"))
}
if waitErr == nil {
t.Process = nil
}
}
// Get rid of the entire data dir
if t.DeleteDataDirOnClose {
if err := os.RemoveAll(t.DataDir); err != nil {
errs = append(errs, fmt.Errorf("Failed to remove data dir %v: %v", t.DataDir, err))
}
}
// Combine the errors if present
if len(errs) == 0 {
return nil
} else if len(errs) == 1 {
t.Debugf("Error while closing Tor: %v", errs[0])
return errs[0]
}
t.Debugf("Errors while closing Tor: %v", errs)
return fmt.Errorf("Got %v errors while closing - %v", len(errs), errs)
}

2
vendor/github.com/cretz/bine/torutil/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package torutil has generic utilities shared across the library.
package torutil

189
vendor/github.com/cretz/bine/torutil/ed25519/ed25519.go generated vendored Normal file
View File

@ -0,0 +1,189 @@
// Package ed25519 implements Tor/BitTorrent-like ed25519 keys.
//
// See the following stack overflow post for details on why
// golang.org/x/crypto/ed25519 can't be used:
// https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
package ed25519
import (
"crypto"
"crypto/rand"
"crypto/sha512"
"errors"
"io"
"github.com/cretz/bine/torutil/ed25519/internal/edwards25519"
"golang.org/x/crypto/ed25519"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
)
// PrivateKey is a 64-byte Ed25519 private key. Unlike
// golang.org/x/crypto/ed25519, this is just the digest and does not contain
// the public key within it. Instead call PublicKey() or better, call KeyPair()
// which stores the precomputed public key.
type PrivateKey []byte
// PublicKey is a 32-byte Ed25519 public key.
type PublicKey []byte
// FromCryptoPrivateKey converts a Go private key to the one in this package.
func FromCryptoPrivateKey(key ed25519.PrivateKey) KeyPair {
digest := sha512.Sum512(key[:32])
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
return &precomputedKeyPair{PrivateKeyBytes: digest[:], PublicKeyBytes: PublicKey(key[32:])}
}
// FromCryptoPublicKey converts a Go public key to the one in this package.
func FromCryptoPublicKey(key ed25519.PublicKey) PublicKey {
return PublicKey(key)
}
// KeyPair returns a new key pair with the public key precomputed.
func (p PrivateKey) KeyPair() KeyPair {
return &precomputedKeyPair{PrivateKeyBytes: p, PublicKeyBytes: p.PublicKey()}
}
// PrivateKey simply returns itself. Implements KeyPair.PrivateKey.
func (p PrivateKey) PrivateKey() PrivateKey { return p }
// Public simply delegates to PublicKey() to satisfy crypto.Signer. This method
// does a bit more work than the traditional Go ed25519's private key's Public()
// method so developers are encouraged to reuse the result or use KeyPair()
// which stores this value.
func (p PrivateKey) Public() crypto.PublicKey { return p.PublicKey() }
// PublicKey generates a public key for this private key. This method does a bit
// more work than the traditional Go ed25519's private key's Public() method so
// developers are encouraged to reuse the result or use KeyPair() which stores
// this value. Implements KeyPair.PublicKey.
func (p PrivateKey) PublicKey() PublicKey {
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], p[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
return publicKeyBytes[:]
}
// Sign signs the given message with priv. Ed25519 performs two passes over
// messages to be signed and therefore cannot handle pre-hashed messages. Thus
// opts.HashFunc() must return zero to indicate the message hasn't been hashed.
// This can be achieved by passing crypto.Hash(0) as the value for opts.
func (p PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(p, message), nil
}
// Verify simply calls PublicKey().Verify(). Callers are encouraged to instead
// store a precomputed KeyPair (via KeyPair() or GenerateKey()) and call Verify
// on that.
func (p PrivateKey) Verify(message []byte, sig []byte) bool {
return p.PublicKey().Verify(message, sig)
}
// Verify simply calls the package-level function Verify().
func (p PublicKey) Verify(message []byte, sig []byte) bool {
return Verify(p, message, sig)
}
// KeyPair is an interface for types with both keys. While PrivateKey does
// implement this, it generates the PublicKey on demand. For better performance,
// use the result of GenerateKey directly or call PrivateKey.KeyPair().
type KeyPair interface {
crypto.Signer
PrivateKey() PrivateKey
PublicKey() PublicKey
Verify(message []byte, sig []byte) bool
}
type precomputedKeyPair struct {
PrivateKeyBytes PrivateKey
PublicKeyBytes PublicKey
}
func (p *precomputedKeyPair) PrivateKey() PrivateKey { return p.PrivateKeyBytes }
func (p *precomputedKeyPair) PublicKey() PublicKey { return p.PublicKeyBytes }
func (p *precomputedKeyPair) Public() crypto.PublicKey { return p.PublicKey() }
func (p *precomputedKeyPair) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(p, message), nil
}
func (p *precomputedKeyPair) Verify(message []byte, sig []byte) bool {
return p.PublicKeyBytes.Verify(message, sig)
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rnd io.Reader) (KeyPair, error) {
if rnd == nil {
rnd = rand.Reader
}
rndByts := make([]byte, 32)
if _, err := io.ReadFull(rnd, rndByts); err != nil {
return nil, err
}
digest := sha512.Sum512(rndByts)
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
return PrivateKey(digest[:]).KeyPair(), nil
}
// Sign signs the message with the given key pair.
func Sign(keyPair KeyPair, message []byte) []byte {
// Ref: https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
var privateKeyA [32]byte
copy(privateKeyA[:], keyPair.PrivateKey()) // we need this in an array later
var messageDigest, hramDigest [64]byte
h := sha512.New()
h.Write(keyPair.PrivateKey()[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(keyPair.PublicKey())
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced)
signature := make([]byte, 64)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
// Verify verifies a signed message.
func Verify(p PublicKey, message []byte, sig []byte) bool {
return ed25519.Verify(ed25519.PublicKey(p), message, sig)
}

View File

@ -0,0 +1 @@
This is taken from https://github.com/golang/crypto/tree/1a580b3eff7814fc9b40602fd35256c63b50f491/ed25519/internal/edwards25519

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

85
vendor/github.com/cretz/bine/torutil/key.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
package torutil
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base32"
"fmt"
"strings"
"github.com/cretz/bine/torutil/ed25519"
"golang.org/x/crypto/sha3"
)
var serviceIDEncoding = base32.StdEncoding.WithPadding(base32.NoPadding)
// OnionServiceIDFromPrivateKey generates the onion service ID from the given
// private key. This panics if the private key is not a 1024-bit
// crypto/*rsa.PrivateKey or github.com/cretz/bine/torutil/ed25519.KeyPair.
func OnionServiceIDFromPrivateKey(key crypto.PrivateKey) string {
switch k := key.(type) {
case *rsa.PrivateKey:
return OnionServiceIDFromV2PublicKey(&k.PublicKey)
case ed25519.KeyPair:
return OnionServiceIDFromV3PublicKey(k.PublicKey())
}
panic(fmt.Sprintf("Unrecognized private key type: %T", key))
}
// OnionServiceIDFromPublicKey generates the onion service ID from the given
// public key. This panics if the public key is not a 1024-bit
// crypto/*rsa.PublicKey or github.com/cretz/bine/torutil/ed25519.PublicKey.
func OnionServiceIDFromPublicKey(key crypto.PublicKey) string {
switch k := key.(type) {
case *rsa.PublicKey:
return OnionServiceIDFromV2PublicKey(k)
case ed25519.PublicKey:
return OnionServiceIDFromV3PublicKey(k)
}
panic(fmt.Sprintf("Unrecognized public key type: %T", key))
}
// OnionServiceIDFromV2PublicKey generates a V2 service ID for the given
// RSA-1024 public key. Panics if not a 1024-bit key.
func OnionServiceIDFromV2PublicKey(key *rsa.PublicKey) string {
if key.N.BitLen() != 1024 {
panic("RSA key not 1024 bit")
}
h := sha1.New()
h.Write(x509.MarshalPKCS1PublicKey(key))
return strings.ToLower(serviceIDEncoding.EncodeToString(h.Sum(nil)[:10]))
}
// OnionServiceIDFromV3PublicKey generates a V3 service ID for the given
// ED25519 public key.
func OnionServiceIDFromV3PublicKey(key ed25519.PublicKey) string {
checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
var keyBytes [35]byte
copy(keyBytes[:], key)
keyBytes[32] = checkSum[0]
keyBytes[33] = checkSum[1]
keyBytes[34] = 0x03
return strings.ToLower(serviceIDEncoding.EncodeToString(keyBytes[:]))
}
// PublicKeyFromV3OnionServiceID returns a public key for the given service ID
// or an error if the service ID is invalid.
func PublicKeyFromV3OnionServiceID(id string) (ed25519.PublicKey, error) {
byts, err := serviceIDEncoding.DecodeString(strings.ToUpper(id))
if err != nil {
return nil, err
} else if len(byts) != 35 {
return nil, fmt.Errorf("Invalid id length")
} else if byts[34] != 0x03 {
return nil, fmt.Errorf("Invalid version")
}
// Do a checksum check
key := ed25519.PublicKey(byts[:32])
checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
if byts[32] != checkSum[0] || byts[33] != checkSum[1] {
return nil, fmt.Errorf("Invalid checksum")
}
return key, nil
}

112
vendor/github.com/cretz/bine/torutil/string.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package torutil
import (
"fmt"
"strings"
)
// PartitionString returns the two parts of a string delimited by the first
// occurrence of ch. If ch does not exist, the second string is empty and the
// resulting bool is false. Otherwise it is true.
func PartitionString(str string, ch byte) (string, string, bool) {
index := strings.IndexByte(str, ch)
if index == -1 {
return str, "", false
}
return str[:index], str[index+1:], true
}
// PartitionStringFromEnd is same as PartitionString except it delimts by the
// last occurrence of ch instead of the first.
func PartitionStringFromEnd(str string, ch byte) (string, string, bool) {
index := strings.LastIndexByte(str, ch)
if index == -1 {
return str, "", false
}
return str[:index], str[index+1:], true
}
// EscapeSimpleQuotedStringIfNeeded calls EscapeSimpleQuotedString only if the
// string contains a space, backslash, double quote, newline, or carriage return
// character.
func EscapeSimpleQuotedStringIfNeeded(str string) string {
if strings.ContainsAny(str, " \\\"\r\n") {
return EscapeSimpleQuotedString(str)
}
return str
}
var simpleQuotedStringEscapeReplacer = strings.NewReplacer(
"\\", "\\\\",
"\"", "\\\"",
"\r", "\\r",
"\n", "\\n",
)
// EscapeSimpleQuotedString calls EscapeSimpleQuotedStringContents and then
// surrounds the entire string with double quotes.
func EscapeSimpleQuotedString(str string) string {
return "\"" + EscapeSimpleQuotedStringContents(str) + "\""
}
// EscapeSimpleQuotedStringContents escapes backslashes, double quotes,
// newlines, and carriage returns in str.
func EscapeSimpleQuotedStringContents(str string) string {
return simpleQuotedStringEscapeReplacer.Replace(str)
}
// UnescapeSimpleQuotedStringIfNeeded calls UnescapeSimpleQuotedString only if
// str is surrounded with double quotes.
func UnescapeSimpleQuotedStringIfNeeded(str string) (string, error) {
if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
return UnescapeSimpleQuotedString(str)
}
return str, nil
}
// UnescapeSimpleQuotedString removes surrounding double quotes and calls
// UnescapeSimpleQuotedStringContents.
func UnescapeSimpleQuotedString(str string) (string, error) {
if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
return "", fmt.Errorf("Missing quotes")
}
return UnescapeSimpleQuotedStringContents(str[1 : len(str)-1])
}
// UnescapeSimpleQuotedStringContents unescapes backslashes, double quotes,
// newlines, and carriage returns. Also errors if those aren't escaped.
func UnescapeSimpleQuotedStringContents(str string) (string, error) {
ret := ""
escaping := false
for _, c := range str {
switch c {
case '\\':
if escaping {
ret += "\\"
}
escaping = !escaping
case '"':
if !escaping {
return "", fmt.Errorf("Unescaped quote")
}
ret += "\""
escaping = false
case '\r', '\n':
return "", fmt.Errorf("Unescaped newline or carriage return")
default:
if escaping {
if c == 'r' {
ret += "\r"
} else if c == 'n' {
ret += "\n"
} else {
return "", fmt.Errorf("Unexpected escape")
}
} else {
ret += string(c)
}
escaping = false
}
}
return ret, nil
}

View File

@ -1,129 +0,0 @@
sudo: false
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- master
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
before_script:
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
- sudo service mysql restart
- .travis/wait_mysql.sh
- mysql -e 'create database gotest;'
matrix:
include:
- env: DB=MYSQL8
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mysql:8.0
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mysql:8.0 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MYSQL57
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mysql:5.7
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MARIA55
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mariadb:5.5
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MARIA10_1
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mariadb:10.1
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- os: osx
osx_image: xcode10.1
addons:
homebrew:
packages:
- mysql
update: true
go: 1.12.x
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
before_script:
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB\nlocal_infile=1" >> /usr/local/etc/my.cnf
- mysql.server start
- mysql -uroot -e 'CREATE USER gotest IDENTIFIED BY "secret"'
- mysql -uroot -e 'GRANT ALL ON *.* TO gotest'
- mysql -uroot -e 'create database gotest;'
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3306
- export MYSQL_TEST_CONCURRENT=1
script:
- go test -v -covermode=count -coverprofile=coverage.out
- go vet ./...
- .travis/gofmt.sh
after_script:
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci

View File

@ -13,12 +13,17 @@
Aaron Hopkins <go-sql-driver at die.net> Aaron Hopkins <go-sql-driver at die.net>
Achille Roussel <achille.roussel at gmail.com> Achille Roussel <achille.roussel at gmail.com>
Alex Snast <alexsn at fb.com>
Alexey Palazhchenko <alexey.palazhchenko at gmail.com> Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
Andrew Reid <andrew.reid at tixtrack.com> Andrew Reid <andrew.reid at tixtrack.com>
Animesh Ray <mail.rayanimesh at gmail.com>
Arne Hormann <arnehormann at gmail.com> Arne Hormann <arnehormann at gmail.com>
Ariel Mashraki <ariel at mashraki.co.il>
Asta Xie <xiemengjun at gmail.com> Asta Xie <xiemengjun at gmail.com>
Bulat Gaifullin <gaifullinbf at gmail.com> Bulat Gaifullin <gaifullinbf at gmail.com>
Caine Jette <jette at alum.mit.edu>
Carlos Nieto <jose.carlos at menteslibres.net> Carlos Nieto <jose.carlos at menteslibres.net>
Chris Kirkland <chriskirkland at github.com>
Chris Moos <chris at tech9computers.com> Chris Moos <chris at tech9computers.com>
Craig Wilson <craiggwilson at gmail.com> Craig Wilson <craiggwilson at gmail.com>
Daniel Montoya <dsmontoyam at gmail.com> Daniel Montoya <dsmontoyam at gmail.com>
@ -41,6 +46,7 @@ Ilia Cimpoes <ichimpoesh at gmail.com>
INADA Naoki <songofacandy at gmail.com> INADA Naoki <songofacandy at gmail.com>
Jacek Szwec <szwec.jacek at gmail.com> Jacek Szwec <szwec.jacek at gmail.com>
James Harr <james.harr at gmail.com> James Harr <james.harr at gmail.com>
Janek Vedock <janekvedock at comcast.net>
Jeff Hodges <jeff at somethingsimilar.com> Jeff Hodges <jeff at somethingsimilar.com>
Jeffrey Charles <jeffreycharles at gmail.com> Jeffrey Charles <jeffreycharles at gmail.com>
Jerome Meyer <jxmeyer at gmail.com> Jerome Meyer <jxmeyer at gmail.com>
@ -52,14 +58,17 @@ Julien Schmidt <go-sql-driver at julienschmidt.com>
Justin Li <jli at j-li.net> Justin Li <jli at j-li.net>
Justin Nuß <nuss.justin at gmail.com> Justin Nuß <nuss.justin at gmail.com>
Kamil Dziedzic <kamil at klecza.pl> Kamil Dziedzic <kamil at klecza.pl>
Kei Kamikawa <x00.x7f.x86 at gmail.com>
Kevin Malachowski <kevin at chowski.com> Kevin Malachowski <kevin at chowski.com>
Kieron Woodhouse <kieron.woodhouse at infosum.com> Kieron Woodhouse <kieron.woodhouse at infosum.com>
Lance Tian <lance6716 at gmail.com>
Lennart Rudolph <lrudolph at hmc.edu> Lennart Rudolph <lrudolph at hmc.edu>
Leonardo YongUk Kim <dalinaum at gmail.com> Leonardo YongUk Kim <dalinaum at gmail.com>
Linh Tran Tuan <linhduonggnu at gmail.com> Linh Tran Tuan <linhduonggnu at gmail.com>
Lion Yang <lion at aosc.xyz> Lion Yang <lion at aosc.xyz>
Luca Looz <luca.looz92 at gmail.com> Luca Looz <luca.looz92 at gmail.com>
Lucas Liu <extrafliu at gmail.com> Lucas Liu <extrafliu at gmail.com>
Lunny Xiao <xiaolunwen at gmail.com>
Luke Scott <luke at webconnex.com> Luke Scott <luke at webconnex.com>
Maciej Zimnoch <maciej.zimnoch at codilime.com> Maciej Zimnoch <maciej.zimnoch at codilime.com>
Michael Woolnough <michael.woolnough at gmail.com> Michael Woolnough <michael.woolnough at gmail.com>
@ -69,31 +78,42 @@ Olivier Mengué <dolmen at cpan.org>
oscarzhao <oscarzhaosl at gmail.com> oscarzhao <oscarzhaosl at gmail.com>
Paul Bonser <misterpib at gmail.com> Paul Bonser <misterpib at gmail.com>
Peter Schultz <peter.schultz at classmarkets.com> Peter Schultz <peter.schultz at classmarkets.com>
Phil Porada <philporada at gmail.com>
Rebecca Chin <rchin at pivotal.io> Rebecca Chin <rchin at pivotal.io>
Reed Allman <rdallman10 at gmail.com> Reed Allman <rdallman10 at gmail.com>
Richard Wilkes <wilkes at me.com> Richard Wilkes <wilkes at me.com>
Robert Russell <robert at rrbrussell.com> Robert Russell <robert at rrbrussell.com>
Runrioter Wung <runrioter at gmail.com> Runrioter Wung <runrioter at gmail.com>
Samantha Frank <hello at entropy.cat>
Santhosh Kumar Tekuri <santhosh.tekuri at gmail.com>
Sho Iizuka <sho.i518 at gmail.com>
Sho Ikeda <suicaicoca at gmail.com>
Shuode Li <elemount at qq.com> Shuode Li <elemount at qq.com>
Simon J Mudd <sjmudd at pobox.com> Simon J Mudd <sjmudd at pobox.com>
Soroush Pour <me at soroushjp.com> Soroush Pour <me at soroushjp.com>
Stan Putrya <root.vagner at gmail.com> Stan Putrya <root.vagner at gmail.com>
Stanley Gunawan <gunawan.stanley at gmail.com> Stanley Gunawan <gunawan.stanley at gmail.com>
Steven Hartland <steven.hartland at multiplay.co.uk> Steven Hartland <steven.hartland at multiplay.co.uk>
Tan Jinhua <312841925 at qq.com>
Thomas Wodarek <wodarekwebpage at gmail.com> Thomas Wodarek <wodarekwebpage at gmail.com>
Tim Ruffles <timruffles at gmail.com> Tim Ruffles <timruffles at gmail.com>
Tom Jenkinson <tom at tjenkinson.me> Tom Jenkinson <tom at tjenkinson.me>
Vladimir Kovpak <cn007b at gmail.com> Vladimir Kovpak <cn007b at gmail.com>
Vladyslav Zhelezniak <zhvladi at gmail.com>
Xiangyu Hu <xiangyu.hu at outlook.com> Xiangyu Hu <xiangyu.hu at outlook.com>
Xiaobing Jiang <s7v7nislands at gmail.com> Xiaobing Jiang <s7v7nislands at gmail.com>
Xiuming Chen <cc at cxm.cc> Xiuming Chen <cc at cxm.cc>
Xuehong Chan <chanxuehong at gmail.com>
Zhenye Xie <xiezhenye at gmail.com> Zhenye Xie <xiezhenye at gmail.com>
Zhixin Wen <john.wenzhixin at gmail.com>
Ziheng Lyu <zihenglv at gmail.com>
# Organizations # Organizations
Barracuda Networks, Inc. Barracuda Networks, Inc.
Counting Ltd. Counting Ltd.
DigitalOcean Inc. DigitalOcean Inc.
dyves labs AG
Facebook Inc. Facebook Inc.
GitHub Inc. GitHub Inc.
Google Inc. Google Inc.
@ -103,3 +123,4 @@ Multiplay Ltd.
Percona LLC Percona LLC
Pivotal Inc. Pivotal Inc.
Stripe Inc. Stripe Inc.
Zendesk Inc.

View File

@ -1,3 +1,63 @@
## Version 1.7.1 (2023-04-25)
Changes:
- bump actions/checkout@v3 and actions/setup-go@v3 (#1375)
- Add go1.20 and mariadb10.11 to the testing matrix (#1403)
- Increase default maxAllowedPacket size. (#1411)
Bugfixes:
- Use SET syntax as specified in the MySQL documentation (#1402)
## Version 1.7 (2022-11-29)
Changes:
- Drop support of Go 1.12 (#1211)
- Refactoring `(*textRows).readRow` in a more clear way (#1230)
- util: Reduce boundary check in escape functions. (#1316)
- enhancement for mysqlConn handleAuthResult (#1250)
New Features:
- support Is comparison on MySQLError (#1210)
- return unsigned in database type name when necessary (#1238)
- Add API to express like a --ssl-mode=PREFERRED MySQL client (#1370)
- Add SQLState to MySQLError (#1321)
Bugfixes:
- Fix parsing 0 year. (#1257)
## Version 1.6 (2021-04-01)
Changes:
- Migrate the CI service from travis-ci to GitHub Actions (#1176, #1183, #1190)
- `NullTime` is deprecated (#960, #1144)
- Reduce allocations when building SET command (#1111)
- Performance improvement for time formatting (#1118)
- Performance improvement for time parsing (#1098, #1113)
New Features:
- Implement `driver.Validator` interface (#1106, #1174)
- Support returning `uint64` from `Valuer` in `ConvertValue` (#1143)
- Add `json.RawMessage` for converter and prepared statement (#1059)
- Interpolate `json.RawMessage` as `string` (#1058)
- Implements `CheckNamedValue` (#1090)
Bugfixes:
- Stop rounding times (#1121, #1172)
- Put zero filler into the SSL handshake packet (#1066)
- Fix checking cancelled connections back into the connection pool (#1095)
- Fix remove last 0 byte for mysql_old_password when password is empty (#1133)
## Version 1.5 (2020-01-07) ## Version 1.5 (2020-01-07)
Changes: Changes:

View File

@ -35,12 +35,12 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
* Supports queries larger than 16MB * Supports queries larger than 16MB
* Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support. * Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
* Intelligent `LONG DATA` handling in prepared statements * Intelligent `LONG DATA` handling in prepared statements
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support * Secure `LOAD DATA LOCAL INFILE` support with file allowlisting and `io.Reader` support
* Optional `time.Time` parsing * Optional `time.Time` parsing
* Optional placeholder interpolation * Optional placeholder interpolation
## Requirements ## Requirements
* Go 1.10 or higher. We aim to support the 3 latest versions of Go. * Go 1.13 or higher. We aim to support the 3 latest versions of Go.
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
--------------------------------------- ---------------------------------------
@ -56,15 +56,37 @@ Make sure [Git is installed](https://git-scm.com/downloads) on your machine and
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then. _Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`: Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
```go ```go
import "database/sql" import (
import _ "github.com/go-sql-driver/mysql" "database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
)
// ...
db, err := sql.Open("mysql", "user:password@/dbname") db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
// See "Important settings" section.
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
``` ```
[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). [Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
### Important settings
`db.SetConnMaxLifetime()` is required to ensure connections are closed by the driver safely before connection is closed by MySQL server, OS, or other middlewares. Since some middlewares close idle connections by 5 minutes, we recommend timeout shorter than 5 minutes. This setting helps load balancing and changing system variables too.
`db.SetMaxOpenConns()` is highly recommended to limit the number of connection used by the application. There is no recommended limit number because it depends on application and MySQL server.
`db.SetMaxIdleConns()` is recommended to be set same to `db.SetMaxOpenConns()`. When it is smaller than `SetMaxOpenConns()`, connections can be opened and closed much more frequently than you expect. Idle connections can be closed by the `db.SetConnMaxLifetime()`. If you want to close idle connections more rapidly, you can use `db.SetConnMaxIdleTime()` since Go 1.15.
### DSN (Data Source Name) ### DSN (Data Source Name)
@ -122,7 +144,7 @@ Valid Values: true, false
Default: false Default: false
``` ```
`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. `allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files.
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
##### `allowCleartextPasswords` ##### `allowCleartextPasswords`
@ -133,7 +155,18 @@ Valid Values: true, false
Default: false Default: false
``` ```
`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network. `allowCleartextPasswords=true` allows using the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
##### `allowFallbackToPlaintext`
```
Type: bool
Valid Values: true, false
Default: false
```
`allowFallbackToPlaintext=true` acts like a `--ssl-mode=PREFERRED` MySQL client as described in [Command Options for Connecting to the Server](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode)
##### `allowNativePasswords` ##### `allowNativePasswords`
@ -230,7 +263,7 @@ Default: false
If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`. If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* *This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are rejected as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
##### `loc` ##### `loc`
@ -249,10 +282,10 @@ Please keep in mind, that param values must be [url.QueryEscape](https://golang.
##### `maxAllowedPacket` ##### `maxAllowedPacket`
``` ```
Type: decimal number Type: decimal number
Default: 4194304 Default: 64*1024*1024
``` ```
Max packet size allowed in bytes. The default value is 4 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*. Max packet size allowed in bytes. The default value is 64 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*.
##### `multiStatements` ##### `multiStatements`
@ -376,7 +409,7 @@ Rules:
Examples: Examples:
* `autocommit=1`: `SET autocommit=1` * `autocommit=1`: `SET autocommit=1`
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'` * [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
* [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'` * [`transaction_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation): `SET transaction_isolation='REPEATABLE-READ'`
#### Examples #### Examples
@ -432,7 +465,7 @@ user:password@/
The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively. The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
## `ColumnType` Support ## `ColumnType` Support
This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `BIGINT`.
## `context.Context` Support ## `context.Context` Support
Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts. Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
@ -445,7 +478,7 @@ For this feature you need direct access to the package. Therefore you must chang
import "github.com/go-sql-driver/mysql" import "github.com/go-sql-driver/mysql"
``` ```
Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore. To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
@ -459,8 +492,6 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes). **Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
### Unicode support ### Unicode support
Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default. Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
@ -477,7 +508,7 @@ To run the driver tests you may need to adjust the configuration. See the [Testi
Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated. Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls). If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details. See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/.github/CONTRIBUTING.md) for details.
--------------------------------------- ---------------------------------------
@ -498,4 +529,3 @@ Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE). You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow") ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")

19
vendor/github.com/go-sql-driver/mysql/atomic_bool.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
//
// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build go1.19
// +build go1.19
package mysql
import "sync/atomic"
/******************************************************************************
* Sync utils *
******************************************************************************/
type atomicBool = atomic.Bool

View File

@ -0,0 +1,47 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
//
// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build !go1.19
// +build !go1.19
package mysql
import "sync/atomic"
/******************************************************************************
* Sync utils *
******************************************************************************/
// atomicBool is an implementation of atomic.Bool for older version of Go.
// it is a wrapper around uint32 for usage as a boolean value with
// atomic access.
type atomicBool struct {
_ noCopy
value uint32
}
// Load returns whether the current boolean value is true
func (ab *atomicBool) Load() bool {
return atomic.LoadUint32(&ab.value) > 0
}
// Store sets the value of the bool regardless of the previous value
func (ab *atomicBool) Store(value bool) {
if value {
atomic.StoreUint32(&ab.value, 1)
} else {
atomic.StoreUint32(&ab.value, 0)
}
}
// Swap sets the value of the bool and returns the old value.
func (ab *atomicBool) Swap(value bool) bool {
if value {
return atomic.SwapUint32(&ab.value, 1) > 0
}
return atomic.SwapUint32(&ab.value, 0) > 0
}

View File

@ -15,6 +15,7 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"fmt"
"sync" "sync"
) )
@ -52,7 +53,6 @@ var (
// } else { // } else {
// log.Fatal("not a RSA public key") // log.Fatal("not a RSA public key")
// } // }
//
func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) { func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) {
serverPubKeyLock.Lock() serverPubKeyLock.Lock()
if serverPubKeyRegistry == nil { if serverPubKeyRegistry == nil {
@ -136,10 +136,6 @@ func pwHash(password []byte) (result [2]uint32) {
// Hash password using insecure pre 4.1 method // Hash password using insecure pre 4.1 method
func scrambleOldPassword(scramble []byte, password string) []byte { func scrambleOldPassword(scramble []byte, password string) []byte {
if len(password) == 0 {
return nil
}
scramble = scramble[:8] scramble = scramble[:8]
hashPw := pwHash([]byte(password)) hashPw := pwHash([]byte(password))
@ -247,6 +243,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
if !mc.cfg.AllowOldPasswords { if !mc.cfg.AllowOldPasswords {
return nil, ErrOldPassword return nil, ErrOldPassword
} }
if len(mc.cfg.Passwd) == 0 {
return nil, nil
}
// Note: there are edge cases where this should work but doesn't; // Note: there are edge cases where this should work but doesn't;
// this is currently "wontfix": // this is currently "wontfix":
// https://github.com/go-sql-driver/mysql/issues/184 // https://github.com/go-sql-driver/mysql/issues/184
@ -274,7 +273,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
if len(mc.cfg.Passwd) == 0 { if len(mc.cfg.Passwd) == 0 {
return []byte{0}, nil return []byte{0}, nil
} }
if mc.cfg.tls != nil || mc.cfg.Net == "unix" { // unlike caching_sha2_password, sha256_password does not accept
// cleartext password on unix transport.
if mc.cfg.TLS != nil {
// write cleartext auth packet // write cleartext auth packet
return append([]byte(mc.cfg.Passwd), 0), nil return append([]byte(mc.cfg.Passwd), 0), nil
} }
@ -350,7 +351,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
} }
case cachingSha2PasswordPerformFullAuthentication: case cachingSha2PasswordPerformFullAuthentication:
if mc.cfg.tls != nil || mc.cfg.Net == "unix" { if mc.cfg.TLS != nil || mc.cfg.Net == "unix" {
// write cleartext auth packet // write cleartext auth packet
err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0)) err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
if err != nil { if err != nil {
@ -365,14 +366,24 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
return err return err
} }
data[4] = cachingSha2PasswordRequestPublicKey data[4] = cachingSha2PasswordRequestPublicKey
mc.writePacket(data) err = mc.writePacket(data)
if err != nil {
return err
}
// parse public key
if data, err = mc.readPacket(); err != nil { if data, err = mc.readPacket(); err != nil {
return err return err
} }
block, _ := pem.Decode(data[1:]) if data[0] != iAuthMoreData {
return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication")
}
// parse public key
block, rest := pem.Decode(data[1:])
if block == nil {
return fmt.Errorf("No Pem data found, data: %s", rest)
}
pkix, err := x509.ParsePKIXPublicKey(block.Bytes) pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
return err return err
@ -401,6 +412,10 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
return nil // auth successful return nil // auth successful
default: default:
block, _ := pem.Decode(authData) block, _ := pem.Decode(authData)
if block == nil {
return fmt.Errorf("no Pem data found, data: %s", authData)
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes) pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
return err return err

View File

@ -13,6 +13,7 @@ const binaryCollation = "binary"
// A list of available collations mapped to the internal ID. // A list of available collations mapped to the internal ID.
// To update this map use the following MySQL query: // To update this map use the following MySQL query:
//
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID // SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID
// //
// Handshake packet have only 1 byte for collation_id. So we can't use collations with ID > 255. // Handshake packet have only 1 byte for collation_id. So we can't use collations with ID > 255.
@ -247,7 +248,7 @@ var collations = map[string]byte{
"utf8mb4_0900_ai_ci": 255, "utf8mb4_0900_ai_ci": 255,
} }
// A blacklist of collations which is unsafe to interpolate parameters. // A denylist of collations which is unsafe to interpolate parameters.
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes. // These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
var unsafeCollations = map[string]bool{ var unsafeCollations = map[string]bool{
"big5_chinese_ci": true, "big5_chinese_ci": true,

View File

@ -6,6 +6,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file, // License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/. // You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos // +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
package mysql package mysql

View File

@ -6,6 +6,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file, // License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/. // You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
package mysql package mysql

View File

@ -12,6 +12,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/json"
"io" "io"
"net" "net"
"strconv" "strconv"
@ -46,9 +47,10 @@ type mysqlConn struct {
// Handles parameters set in DSN after the connection is established // Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) { func (mc *mysqlConn) handleParams() (err error) {
var cmdSet strings.Builder
for param, val := range mc.cfg.Params { for param, val := range mc.cfg.Params {
switch param { switch param {
// Charset // Charset: character_set_connection, character_set_client, character_set_results
case "charset": case "charset":
charsets := strings.Split(val, ",") charsets := strings.Split(val, ",")
for i := range charsets { for i := range charsets {
@ -62,14 +64,27 @@ func (mc *mysqlConn) handleParams() (err error) {
return return
} }
// System Vars // Other system vars accumulated in a single SET command
default: default:
err = mc.exec("SET " + param + "=" + val + "") if cmdSet.Len() == 0 {
// Heuristic: 29 chars for each other key=value to reduce reallocations
cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1))
cmdSet.WriteString("SET ")
} else {
cmdSet.WriteString(", ")
}
cmdSet.WriteString(param)
cmdSet.WriteString(" = ")
cmdSet.WriteString(val)
}
}
if cmdSet.Len() > 0 {
err = mc.exec(cmdSet.String())
if err != nil { if err != nil {
return return
} }
} }
}
return return
} }
@ -89,7 +104,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
} }
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) { func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
if mc.closed.IsSet() { if mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -108,7 +123,7 @@ func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
func (mc *mysqlConn) Close() (err error) { func (mc *mysqlConn) Close() (err error) {
// Makes Close idempotent // Makes Close idempotent
if !mc.closed.IsSet() { if !mc.closed.Load() {
err = mc.writeCommandPacket(comQuit) err = mc.writeCommandPacket(comQuit)
} }
@ -122,7 +137,7 @@ func (mc *mysqlConn) Close() (err error) {
// is called before auth or on auth failure because MySQL will have already // is called before auth or on auth failure because MySQL will have already
// closed the network connection. // closed the network connection.
func (mc *mysqlConn) cleanup() { func (mc *mysqlConn) cleanup() {
if !mc.closed.TrySet(true) { if mc.closed.Swap(true) {
return return
} }
@ -137,7 +152,7 @@ func (mc *mysqlConn) cleanup() {
} }
func (mc *mysqlConn) error() error { func (mc *mysqlConn) error() error {
if mc.closed.IsSet() { if mc.closed.Load() {
if err := mc.canceled.Value(); err != nil { if err := mc.canceled.Value(); err != nil {
return err return err
} }
@ -147,7 +162,7 @@ func (mc *mysqlConn) error() error {
} }
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
if mc.closed.IsSet() { if mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -230,47 +245,21 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
if v.IsZero() { if v.IsZero() {
buf = append(buf, "'0000-00-00'"...) buf = append(buf, "'0000-00-00'"...)
} else { } else {
v := v.In(mc.cfg.Loc) buf = append(buf, '\'')
v = v.Add(time.Nanosecond * 500) // To round under microsecond buf, err = appendDateTime(buf, v.In(mc.cfg.Loc))
year := v.Year() if err != nil {
year100 := year / 100 return "", err
year1 := year % 100
month := v.Month()
day := v.Day()
hour := v.Hour()
minute := v.Minute()
second := v.Second()
micro := v.Nanosecond() / 1000
buf = append(buf, []byte{
'\'',
digits10[year100], digits01[year100],
digits10[year1], digits01[year1],
'-',
digits10[month], digits01[month],
'-',
digits10[day], digits01[day],
' ',
digits10[hour], digits01[hour],
':',
digits10[minute], digits01[minute],
':',
digits10[second], digits01[second],
}...)
if micro != 0 {
micro10000 := micro / 10000
micro100 := micro / 100 % 100
micro1 := micro % 100
buf = append(buf, []byte{
'.',
digits10[micro10000], digits01[micro10000],
digits10[micro100], digits01[micro100],
digits10[micro1], digits01[micro1],
}...)
} }
buf = append(buf, '\'') buf = append(buf, '\'')
} }
case json.RawMessage:
buf = append(buf, '\'')
if mc.status&statusNoBackslashEscapes == 0 {
buf = escapeBytesBackslash(buf, v)
} else {
buf = escapeBytesQuotes(buf, v)
}
buf = append(buf, '\'')
case []byte: case []byte:
if v == nil { if v == nil {
buf = append(buf, "NULL"...) buf = append(buf, "NULL"...)
@ -306,7 +295,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
} }
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
if mc.closed.IsSet() { if mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -367,7 +356,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
} }
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) { func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
if mc.closed.IsSet() { if mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -461,7 +450,7 @@ func (mc *mysqlConn) finish() {
// Ping implements driver.Pinger interface // Ping implements driver.Pinger interface
func (mc *mysqlConn) Ping(ctx context.Context) (err error) { func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
if mc.closed.IsSet() { if mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return driver.ErrBadConn return driver.ErrBadConn
} }
@ -480,6 +469,10 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
// BeginTx implements driver.ConnBeginTx interface // BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if mc.closed.Load() {
return nil, driver.ErrBadConn
}
if err := mc.watchCancel(ctx); err != nil { if err := mc.watchCancel(ctx); err != nil {
return nil, err return nil, err
} }
@ -643,9 +636,15 @@ func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
// ResetSession implements driver.SessionResetter. // ResetSession implements driver.SessionResetter.
// (From Go 1.10) // (From Go 1.10)
func (mc *mysqlConn) ResetSession(ctx context.Context) error { func (mc *mysqlConn) ResetSession(ctx context.Context) error {
if mc.closed.IsSet() { if mc.closed.Load() {
return driver.ErrBadConn return driver.ErrBadConn
} }
mc.reset = true mc.reset = true
return nil return nil
} }
// IsValid implements driver.Validator interface
// (From Go 1.15)
func (mc *mysqlConn) IsValid() bool {
return !mc.closed.Load()
}

View File

@ -10,7 +10,7 @@ package mysql
const ( const (
defaultAuthPlugin = "mysql_native_password" defaultAuthPlugin = "mysql_native_password"
defaultMaxAllowedPacket = 4 << 20 // 4 MiB defaultMaxAllowedPacket = 64 << 20 // 64 MiB. See https://github.com/go-sql-driver/mysql/issues/1355
minProtocolVersion = 10 minProtocolVersion = 10
maxPacketSize = 1<<24 - 1 maxPacketSize = 1<<24 - 1
timeFormat = "2006-01-02 15:04:05.999999" timeFormat = "2006-01-02 15:04:05.999999"

View File

@ -46,13 +46,14 @@ type Config struct {
ServerPubKey string // Server public key name ServerPubKey string // Server public key name
pubKey *rsa.PublicKey // Server public key pubKey *rsa.PublicKey // Server public key
TLSConfig string // TLS configuration name TLSConfig string // TLS configuration name
tls *tls.Config // TLS configuration TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig
Timeout time.Duration // Dial timeout Timeout time.Duration // Dial timeout
ReadTimeout time.Duration // I/O read timeout ReadTimeout time.Duration // I/O read timeout
WriteTimeout time.Duration // I/O write timeout WriteTimeout time.Duration // I/O write timeout
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
AllowCleartextPasswords bool // Allows the cleartext client side plugin AllowCleartextPasswords bool // Allows the cleartext client side plugin
AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
AllowNativePasswords bool // Allows the native password authentication method AllowNativePasswords bool // Allows the native password authentication method
AllowOldPasswords bool // Allows the old insecure password method AllowOldPasswords bool // Allows the old insecure password method
CheckConnLiveness bool // Check connections for liveness before using them CheckConnLiveness bool // Check connections for liveness before using them
@ -77,8 +78,8 @@ func NewConfig() *Config {
func (cfg *Config) Clone() *Config { func (cfg *Config) Clone() *Config {
cp := *cfg cp := *cfg
if cp.tls != nil { if cp.TLS != nil {
cp.tls = cfg.tls.Clone() cp.TLS = cfg.TLS.Clone()
} }
if len(cp.Params) > 0 { if len(cp.Params) > 0 {
cp.Params = make(map[string]string, len(cfg.Params)) cp.Params = make(map[string]string, len(cfg.Params))
@ -119,24 +120,29 @@ func (cfg *Config) normalize() error {
cfg.Addr = ensureHavePort(cfg.Addr) cfg.Addr = ensureHavePort(cfg.Addr)
} }
if cfg.TLS == nil {
switch cfg.TLSConfig { switch cfg.TLSConfig {
case "false", "": case "false", "":
// don't set anything // don't set anything
case "true": case "true":
cfg.tls = &tls.Config{} cfg.TLS = &tls.Config{}
case "skip-verify", "preferred": case "skip-verify":
cfg.tls = &tls.Config{InsecureSkipVerify: true} cfg.TLS = &tls.Config{InsecureSkipVerify: true}
case "preferred":
cfg.TLS = &tls.Config{InsecureSkipVerify: true}
cfg.AllowFallbackToPlaintext = true
default: default:
cfg.tls = getTLSConfigClone(cfg.TLSConfig) cfg.TLS = getTLSConfigClone(cfg.TLSConfig)
if cfg.tls == nil { if cfg.TLS == nil {
return errors.New("invalid value / unknown config name: " + cfg.TLSConfig) return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
} }
} }
}
if cfg.tls != nil && cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify { if cfg.TLS != nil && cfg.TLS.ServerName == "" && !cfg.TLS.InsecureSkipVerify {
host, _, err := net.SplitHostPort(cfg.Addr) host, _, err := net.SplitHostPort(cfg.Addr)
if err == nil { if err == nil {
cfg.tls.ServerName = host cfg.TLS.ServerName = host
} }
} }
@ -204,6 +210,10 @@ func (cfg *Config) FormatDSN() string {
writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true") writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true")
} }
if cfg.AllowFallbackToPlaintext {
writeDSNParam(&buf, &hasParam, "allowFallbackToPlaintext", "true")
}
if !cfg.AllowNativePasswords { if !cfg.AllowNativePasswords {
writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false") writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false")
} }
@ -375,7 +385,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
// cfg params // cfg params
switch value := param[1]; param[0] { switch value := param[1]; param[0] {
// Disable INFILE whitelist / enable all files // Disable INFILE allowlist / enable all files
case "allowAllFiles": case "allowAllFiles":
var isBool bool var isBool bool
cfg.AllowAllFiles, isBool = readBool(value) cfg.AllowAllFiles, isBool = readBool(value)
@ -391,6 +401,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
return errors.New("invalid bool value: " + value) return errors.New("invalid bool value: " + value)
} }
// Allow fallback to unencrypted connection if server does not support TLS
case "allowFallbackToPlaintext":
var isBool bool
cfg.AllowFallbackToPlaintext, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Use native password authentication // Use native password authentication
case "allowNativePasswords": case "allowNativePasswords":
var isBool bool var isBool bool
@ -426,7 +444,6 @@ func parseDSNParams(cfg *Config, params string) (err error) {
// Collation // Collation
case "collation": case "collation":
cfg.Collation = value cfg.Collation = value
break
case "columnsWithAlias": case "columnsWithAlias":
var isBool bool var isBool bool

View File

@ -27,7 +27,7 @@ var (
ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
ErrPktSync = errors.New("commands out of sync. You can't run this command now") ErrPktSync = errors.New("commands out of sync. You can't run this command now")
ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?")
ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`")
ErrBusyBuffer = errors.New("busy buffer") ErrBusyBuffer = errors.New("busy buffer")
// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet. // errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
@ -57,9 +57,21 @@ func SetLogger(logger Logger) error {
// MySQLError is an error type which represents a single MySQL error // MySQLError is an error type which represents a single MySQL error
type MySQLError struct { type MySQLError struct {
Number uint16 Number uint16
SQLState [5]byte
Message string Message string
} }
func (me *MySQLError) Error() string { func (me *MySQLError) Error() string {
if me.SQLState != [5]byte{} {
return fmt.Sprintf("Error %d (%s): %s", me.Number, me.SQLState, me.Message)
}
return fmt.Sprintf("Error %d: %s", me.Number, me.Message) return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
} }
func (me *MySQLError) Is(err error) bool {
if merr, ok := err.(*MySQLError); ok {
return merr.Number == me.Number
}
return false
}

View File

@ -41,6 +41,9 @@ func (mf *mysqlField) typeDatabaseName() string {
case fieldTypeJSON: case fieldTypeJSON:
return "JSON" return "JSON"
case fieldTypeLong: case fieldTypeLong:
if mf.flags&flagUnsigned != 0 {
return "UNSIGNED INT"
}
return "INT" return "INT"
case fieldTypeLongBLOB: case fieldTypeLongBLOB:
if mf.charSet != collations[binaryCollation] { if mf.charSet != collations[binaryCollation] {
@ -48,6 +51,9 @@ func (mf *mysqlField) typeDatabaseName() string {
} }
return "LONGBLOB" return "LONGBLOB"
case fieldTypeLongLong: case fieldTypeLongLong:
if mf.flags&flagUnsigned != 0 {
return "UNSIGNED BIGINT"
}
return "BIGINT" return "BIGINT"
case fieldTypeMediumBLOB: case fieldTypeMediumBLOB:
if mf.charSet != collations[binaryCollation] { if mf.charSet != collations[binaryCollation] {
@ -63,6 +69,9 @@ func (mf *mysqlField) typeDatabaseName() string {
case fieldTypeSet: case fieldTypeSet:
return "SET" return "SET"
case fieldTypeShort: case fieldTypeShort:
if mf.flags&flagUnsigned != 0 {
return "UNSIGNED SMALLINT"
}
return "SMALLINT" return "SMALLINT"
case fieldTypeString: case fieldTypeString:
if mf.charSet == collations[binaryCollation] { if mf.charSet == collations[binaryCollation] {
@ -74,6 +83,9 @@ func (mf *mysqlField) typeDatabaseName() string {
case fieldTypeTimestamp: case fieldTypeTimestamp:
return "TIMESTAMP" return "TIMESTAMP"
case fieldTypeTiny: case fieldTypeTiny:
if mf.flags&flagUnsigned != 0 {
return "UNSIGNED TINYINT"
}
return "TINYINT" return "TINYINT"
case fieldTypeTinyBLOB: case fieldTypeTinyBLOB:
if mf.charSet != collations[binaryCollation] { if mf.charSet != collations[binaryCollation] {
@ -106,7 +118,7 @@ var (
scanTypeInt64 = reflect.TypeOf(int64(0)) scanTypeInt64 = reflect.TypeOf(int64(0))
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
scanTypeNullTime = reflect.TypeOf(NullTime{}) scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
scanTypeUint8 = reflect.TypeOf(uint8(0)) scanTypeUint8 = reflect.TypeOf(uint8(0))
scanTypeUint16 = reflect.TypeOf(uint16(0)) scanTypeUint16 = reflect.TypeOf(uint16(0))
scanTypeUint32 = reflect.TypeOf(uint32(0)) scanTypeUint32 = reflect.TypeOf(uint32(0))

25
vendor/github.com/go-sql-driver/mysql/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
//
// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build gofuzz
// +build gofuzz
package mysql
import (
"database/sql"
)
func Fuzz(data []byte) int {
db, err := sql.Open("mysql", string(data))
if err != nil {
return 0
}
db.Close()
return 1
}

View File

@ -1,3 +0,0 @@
module github.com/go-sql-driver/mysql
go 1.10

View File

@ -23,7 +23,7 @@ var (
readerRegisterLock sync.RWMutex readerRegisterLock sync.RWMutex
) )
// RegisterLocalFile adds the given file to the file whitelist, // RegisterLocalFile adds the given file to the file allowlist,
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>". // so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
// Alternatively you can allow the use of all local files with // Alternatively you can allow the use of all local files with
// the DSN parameter 'allowAllFiles=true' // the DSN parameter 'allowAllFiles=true'
@ -33,7 +33,6 @@ var (
// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo") // err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
// if err != nil { // if err != nil {
// ... // ...
//
func RegisterLocalFile(filePath string) { func RegisterLocalFile(filePath string) {
fileRegisterLock.Lock() fileRegisterLock.Lock()
// lazy map init // lazy map init
@ -45,7 +44,7 @@ func RegisterLocalFile(filePath string) {
fileRegisterLock.Unlock() fileRegisterLock.Unlock()
} }
// DeregisterLocalFile removes the given filepath from the whitelist. // DeregisterLocalFile removes the given filepath from the allowlist.
func DeregisterLocalFile(filePath string) { func DeregisterLocalFile(filePath string) {
fileRegisterLock.Lock() fileRegisterLock.Lock()
delete(fileRegister, strings.Trim(filePath, `"`)) delete(fileRegister, strings.Trim(filePath, `"`))
@ -66,7 +65,6 @@ func DeregisterLocalFile(filePath string) {
// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo") // err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
// if err != nil { // if err != nil {
// ... // ...
//
func RegisterReaderHandler(name string, handler func() io.Reader) { func RegisterReaderHandler(name string, handler func() io.Reader) {
readerRegisterLock.Lock() readerRegisterLock.Lock()
// lazy map init // lazy map init
@ -93,10 +91,12 @@ func deferredClose(err *error, closer io.Closer) {
} }
} }
const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
func (mc *mysqlConn) handleInFileRequest(name string) (err error) { func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
var rdr io.Reader var rdr io.Reader
var data []byte var data []byte
packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP packetSize := defaultPacketSize
if mc.maxWriteSize < packetSize { if mc.maxWriteSize < packetSize {
packetSize = mc.maxWriteSize packetSize = mc.maxWriteSize
} }

View File

@ -9,11 +9,32 @@
package mysql package mysql
import ( import (
"database/sql"
"database/sql/driver" "database/sql/driver"
"fmt" "fmt"
"time" "time"
) )
// NullTime represents a time.Time that may be NULL.
// NullTime implements the Scanner interface so
// it can be used as a scan destination:
//
// var nt NullTime
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
// ...
// if nt.Valid {
// // use nt.Time
// } else {
// // NULL value
// }
//
// # This NullTime implementation is not driver-specific
//
// Deprecated: NullTime doesn't honor the loc DSN parameter.
// NullTime.Scan interprets a time as UTC, not the loc DSN parameter.
// Use sql.NullTime instead.
type NullTime sql.NullTime
// Scan implements the Scanner interface. // Scan implements the Scanner interface.
// The value type must be time.Time or string / []byte (formatted time-string), // The value type must be time.Time or string / []byte (formatted time-string),
// otherwise Scan fails. // otherwise Scan fails.
@ -28,11 +49,11 @@ func (nt *NullTime) Scan(value interface{}) (err error) {
nt.Time, nt.Valid = v, true nt.Time, nt.Valid = v, true
return return
case []byte: case []byte:
nt.Time, err = parseDateTime(string(v), time.UTC) nt.Time, err = parseDateTime(v, time.UTC)
nt.Valid = (err == nil) nt.Valid = (err == nil)
return return
case string: case string:
nt.Time, err = parseDateTime(v, time.UTC) nt.Time, err = parseDateTime([]byte(v), time.UTC)
nt.Valid = (err == nil) nt.Valid = (err == nil)
return return
} }

View File

@ -1,31 +0,0 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build go1.13
package mysql
import (
"database/sql"
)
// NullTime represents a time.Time that may be NULL.
// NullTime implements the Scanner interface so
// it can be used as a scan destination:
//
// var nt NullTime
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
// ...
// if nt.Valid {
// // use nt.Time
// } else {
// // NULL value
// }
//
// This NullTime implementation is not driver-specific
type NullTime sql.NullTime

View File

@ -1,34 +0,0 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build !go1.13
package mysql
import (
"time"
)
// NullTime represents a time.Time that may be NULL.
// NullTime implements the Scanner interface so
// it can be used as a scan destination:
//
// var nt NullTime
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
// ...
// if nt.Valid {
// // use nt.Time
// } else {
// // NULL value
// }
//
// This NullTime implementation is not driver-specific
type NullTime struct {
Time time.Time
Valid bool // Valid is true if Time is not NULL
}

View File

@ -13,6 +13,7 @@ import (
"crypto/tls" "crypto/tls"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -109,15 +110,14 @@ func (mc *mysqlConn) writePacket(data []byte) error {
conn = mc.rawConn conn = mc.rawConn
} }
var err error var err error
// If this connection has a ReadTimeout which we've been setting on if mc.cfg.CheckConnLiveness {
// reads, reset it to its default value before we attempt a non-blocking
// read, otherwise the scheduler will just time us out before we can read
if mc.cfg.ReadTimeout != 0 { if mc.cfg.ReadTimeout != 0 {
err = conn.SetReadDeadline(time.Time{}) err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout))
} }
if err == nil && mc.cfg.CheckConnLiveness { if err == nil {
err = connCheck(conn) err = connCheck(conn)
} }
}
if err != nil { if err != nil {
errLog.Print("closing bad idle connection: ", err) errLog.Print("closing bad idle connection: ", err)
mc.Close() mc.Close()
@ -222,9 +222,9 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
if mc.flags&clientProtocol41 == 0 { if mc.flags&clientProtocol41 == 0 {
return nil, "", ErrOldProtocol return nil, "", ErrOldProtocol
} }
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil { if mc.flags&clientSSL == 0 && mc.cfg.TLS != nil {
if mc.cfg.TLSConfig == "preferred" { if mc.cfg.AllowFallbackToPlaintext {
mc.cfg.tls = nil mc.cfg.TLS = nil
} else { } else {
return nil, "", ErrNoTLS return nil, "", ErrNoTLS
} }
@ -292,7 +292,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
} }
// To enable TLS / SSL // To enable TLS / SSL
if mc.cfg.tls != nil { if mc.cfg.TLS != nil {
clientFlags |= clientSSL clientFlags |= clientSSL
} }
@ -348,16 +348,22 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
return errors.New("unknown collation") return errors.New("unknown collation")
} }
// Filler [23 bytes] (all 0x00)
pos := 13
for ; pos < 13+23; pos++ {
data[pos] = 0
}
// SSL Connection Request Packet // SSL Connection Request Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
if mc.cfg.tls != nil { if mc.cfg.TLS != nil {
// Send TLS / SSL request packet // Send TLS / SSL request packet
if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil { if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
return err return err
} }
// Switch to TLS // Switch to TLS
tlsConn := tls.Client(mc.netConn, mc.cfg.tls) tlsConn := tls.Client(mc.netConn, mc.cfg.TLS)
if err := tlsConn.Handshake(); err != nil { if err := tlsConn.Handshake(); err != nil {
return err return err
} }
@ -366,12 +372,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
mc.buf.nc = tlsConn mc.buf.nc = tlsConn
} }
// Filler [23 bytes] (all 0x00)
pos := 13
for ; pos < 13+23; pos++ {
data[pos] = 0
}
// User [null terminated string] // User [null terminated string]
if len(mc.cfg.User) > 0 { if len(mc.cfg.User) > 0 {
pos += copy(data[pos:], mc.cfg.User) pos += copy(data[pos:], mc.cfg.User)
@ -587,19 +587,20 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
return driver.ErrBadConn return driver.ErrBadConn
} }
me := &MySQLError{Number: errno}
pos := 3 pos := 3
// SQL State [optional: # + 5bytes string] // SQL State [optional: # + 5bytes string]
if data[3] == 0x23 { if data[3] == 0x23 {
//sqlstate := string(data[4 : 4+5]) copy(me.SQLState[:], data[4:4+5])
pos = 9 pos = 9
} }
// Error Message [string] // Error Message [string]
return &MySQLError{ me.Message = string(data[pos:])
Number: errno,
Message: string(data[pos:]), return me
}
} }
func readStatus(b []byte) statusFlag { func readStatus(b []byte) statusFlag {
@ -760,40 +761,40 @@ func (rows *textRows) readRow(dest []driver.Value) error {
} }
// RowSet Packet // RowSet Packet
var n int var (
var isNull bool n int
pos := 0 isNull bool
pos int = 0
)
for i := range dest { for i := range dest {
// Read bytes and convert to string // Read bytes and convert to string
dest[i], isNull, n, err = readLengthEncodedString(data[pos:]) dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
pos += n pos += n
if err == nil {
if !isNull { if err != nil {
if !mc.parseTime { return err
continue
} else {
switch rows.rs.columns[i].fieldType {
case fieldTypeTimestamp, fieldTypeDateTime,
fieldTypeDate, fieldTypeNewDate:
dest[i], err = parseDateTime(
string(dest[i].([]byte)),
mc.cfg.Loc,
)
if err == nil {
continue
}
default:
continue
}
} }
} else { if isNull {
dest[i] = nil dest[i] = nil
continue continue
} }
if !mc.parseTime {
continue
}
// Parse time field
switch rows.rs.columns[i].fieldType {
case fieldTypeTimestamp,
fieldTypeDateTime,
fieldTypeDate,
fieldTypeNewDate:
if dest[i], err = parseDateTime(dest[i].([]byte), mc.cfg.Loc); err != nil {
return err
}
} }
return err // err != nil
} }
return nil return nil
@ -1003,6 +1004,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
continue continue
} }
if v, ok := arg.(json.RawMessage); ok {
arg = []byte(v)
}
// cache types and values // cache types and values
switch v := arg.(type) { switch v := arg.(type) {
case int64: case int64:
@ -1112,7 +1116,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if v.IsZero() { if v.IsZero() {
b = append(b, "0000-00-00"...) b = append(b, "0000-00-00"...)
} else { } else {
b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat) b, err = appendDateTime(b, v.In(mc.cfg.Loc))
if err != nil {
return err
}
} }
paramValues = appendLengthEncodedInteger(paramValues, paramValues = appendLengthEncodedInteger(paramValues,

View File

@ -10,6 +10,7 @@ package mysql
import ( import (
"database/sql/driver" "database/sql/driver"
"encoding/json"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
@ -22,7 +23,7 @@ type mysqlStmt struct {
} }
func (stmt *mysqlStmt) Close() error { func (stmt *mysqlStmt) Close() error {
if stmt.mc == nil || stmt.mc.closed.IsSet() { if stmt.mc == nil || stmt.mc.closed.Load() {
// driver.Stmt.Close can be called more than once, thus this function // driver.Stmt.Close can be called more than once, thus this function
// has to be idempotent. // has to be idempotent.
// See also Issue #450 and golang/go#16019. // See also Issue #450 and golang/go#16019.
@ -43,8 +44,13 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
return converter{} return converter{}
} }
func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
nv.Value, err = converter{}.ConvertValue(nv.Value)
return
}
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
if stmt.mc.closed.IsSet() { if stmt.mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -92,7 +98,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
} }
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) { func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
if stmt.mc.closed.IsSet() { if stmt.mc.closed.Load() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@ -129,6 +135,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
return rows, err return rows, err
} }
var jsonType = reflect.TypeOf(json.RawMessage{})
type converter struct{} type converter struct{}
// ConvertValue mirrors the reference/default converter in database/sql/driver // ConvertValue mirrors the reference/default converter in database/sql/driver
@ -146,12 +154,17 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !driver.IsValue(sv) { if driver.IsValue(sv) {
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
}
return sv, nil return sv, nil
} }
// A value returned from the Valuer interface can be "a type handled by
// a database driver's NamedValueChecker interface" so we should accept
// uint64 here as well.
if u, ok := sv.(uint64); ok {
return u, nil
}
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
}
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
switch rv.Kind() { switch rv.Kind() {
case reflect.Ptr: case reflect.Ptr:
@ -170,11 +183,14 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
case reflect.Bool: case reflect.Bool:
return rv.Bool(), nil return rv.Bool(), nil
case reflect.Slice: case reflect.Slice:
ek := rv.Type().Elem().Kind() switch t := rv.Type(); {
if ek == reflect.Uint8 { case t == jsonType:
return v, nil
case t.Elem().Kind() == reflect.Uint8:
return rv.Bytes(), nil return rv.Bytes(), nil
default:
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
} }
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
case reflect.String: case reflect.String:
return rv.String(), nil return rv.String(), nil
} }

View File

@ -13,7 +13,7 @@ type mysqlTx struct {
} }
func (tx *mysqlTx) Commit() (err error) { func (tx *mysqlTx) Commit() (err error) {
if tx.mc == nil || tx.mc.closed.IsSet() { if tx.mc == nil || tx.mc.closed.Load() {
return ErrInvalidConn return ErrInvalidConn
} }
err = tx.mc.exec("COMMIT") err = tx.mc.exec("COMMIT")
@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
} }
func (tx *mysqlTx) Rollback() (err error) { func (tx *mysqlTx) Rollback() (err error) {
if tx.mc == nil || tx.mc.closed.IsSet() { if tx.mc == nil || tx.mc.closed.Load() {
return ErrInvalidConn return ErrInvalidConn
} }
err = tx.mc.exec("ROLLBACK") err = tx.mc.exec("ROLLBACK")

View File

@ -54,7 +54,6 @@ var (
// Certificates: clientCert, // Certificates: clientCert,
// }) // })
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom") // db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
//
func RegisterTLSConfig(key string, config *tls.Config) error { func RegisterTLSConfig(key string, config *tls.Config) error {
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" || strings.ToLower(key) == "preferred" { if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" || strings.ToLower(key) == "preferred" {
return fmt.Errorf("key '%s' is reserved", key) return fmt.Errorf("key '%s' is reserved", key)
@ -106,27 +105,126 @@ func readBool(input string) (value bool, valid bool) {
* Time related utils * * Time related utils *
******************************************************************************/ ******************************************************************************/
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
base := "0000-00-00 00:00:00.0000000" const base = "0000-00-00 00:00:00.000000"
switch len(str) { switch len(b) {
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
if str == base[:len(str)] { if string(b) == base[:len(b)] {
return return time.Time{}, nil
} }
t, err = time.Parse(timeFormat[:len(str)], str)
year, err := parseByteYear(b)
if err != nil {
return time.Time{}, err
}
if b[4] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4])
}
m, err := parseByte2Digits(b[5], b[6])
if err != nil {
return time.Time{}, err
}
month := time.Month(m)
if b[7] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7])
}
day, err := parseByte2Digits(b[8], b[9])
if err != nil {
return time.Time{}, err
}
if len(b) == 10 {
return time.Date(year, month, day, 0, 0, 0, 0, loc), nil
}
if b[10] != ' ' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[10])
}
hour, err := parseByte2Digits(b[11], b[12])
if err != nil {
return time.Time{}, err
}
if b[13] != ':' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[13])
}
min, err := parseByte2Digits(b[14], b[15])
if err != nil {
return time.Time{}, err
}
if b[16] != ':' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[16])
}
sec, err := parseByte2Digits(b[17], b[18])
if err != nil {
return time.Time{}, err
}
if len(b) == 19 {
return time.Date(year, month, day, hour, min, sec, 0, loc), nil
}
if b[19] != '.' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[19])
}
nsec, err := parseByteNanoSec(b[20:])
if err != nil {
return time.Time{}, err
}
return time.Date(year, month, day, hour, min, sec, nsec, loc), nil
default: default:
err = fmt.Errorf("invalid time string: %s", str) return time.Time{}, fmt.Errorf("invalid time bytes: %s", b)
return
} }
}
// Adjust location func parseByteYear(b []byte) (int, error) {
if err == nil && loc != time.UTC { year, n := 0, 1000
y, mo, d := t.Date() for i := 0; i < 4; i++ {
h, mi, s := t.Clock() v, err := bToi(b[i])
t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil if err != nil {
return 0, err
} }
year += v * n
n /= 10
}
return year, nil
}
return func parseByte2Digits(b1, b2 byte) (int, error) {
d1, err := bToi(b1)
if err != nil {
return 0, err
}
d2, err := bToi(b2)
if err != nil {
return 0, err
}
return d1*10 + d2, nil
}
func parseByteNanoSec(b []byte) (int, error) {
ns, digit := 0, 100000 // max is 6-digits
for i := 0; i < len(b); i++ {
v, err := bToi(b[i])
if err != nil {
return 0, err
}
ns += v * digit
digit /= 10
}
// nanoseconds has 10-digits. (needs to scale digits)
// 10 - 6 = 4, so we have to multiple 1000.
return ns * 1000, nil
}
func bToi(b byte) (int, error) {
if b < '0' || b > '9' {
return 0, errors.New("not [0-9]")
}
return int(b - '0'), nil
} }
func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
@ -167,6 +265,64 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
return nil, fmt.Errorf("invalid DATETIME packet length %d", num) return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
} }
func appendDateTime(buf []byte, t time.Time) ([]byte, error) {
year, month, day := t.Date()
hour, min, sec := t.Clock()
nsec := t.Nanosecond()
if year < 1 || year > 9999 {
return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
}
year100 := year / 100
year1 := year % 100
var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape
localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]
localBuf[4] = '-'
localBuf[5], localBuf[6] = digits10[month], digits01[month]
localBuf[7] = '-'
localBuf[8], localBuf[9] = digits10[day], digits01[day]
if hour == 0 && min == 0 && sec == 0 && nsec == 0 {
return append(buf, localBuf[:10]...), nil
}
localBuf[10] = ' '
localBuf[11], localBuf[12] = digits10[hour], digits01[hour]
localBuf[13] = ':'
localBuf[14], localBuf[15] = digits10[min], digits01[min]
localBuf[16] = ':'
localBuf[17], localBuf[18] = digits10[sec], digits01[sec]
if nsec == 0 {
return append(buf, localBuf[:19]...), nil
}
nsec100000000 := nsec / 100000000
nsec1000000 := (nsec / 1000000) % 100
nsec10000 := (nsec / 10000) % 100
nsec100 := (nsec / 100) % 100
nsec1 := nsec % 100
localBuf[19] = '.'
// milli second
localBuf[20], localBuf[21], localBuf[22] =
digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000]
// micro second
localBuf[23], localBuf[24], localBuf[25] =
digits10[nsec10000], digits01[nsec10000], digits10[nsec100]
// nano second
localBuf[26], localBuf[27], localBuf[28] =
digits01[nsec100], digits10[nsec1], digits01[nsec1]
// trim trailing zeros
n := len(localBuf)
for n > 0 && localBuf[n-1] == '0' {
n--
}
return append(buf, localBuf[:n]...), nil
}
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation // zeroDateTime is used in formatBinaryDateTime to avoid an allocation
// if the DATE or DATETIME has the zero value. // if the DATE or DATETIME has the zero value.
// It must never be changed. // It must never be changed.
@ -375,7 +531,7 @@ func stringToInt(b []byte) int {
return val return val
} }
// returns the string read as a bytes slice, wheter the value is NULL, // returns the string read as a bytes slice, whether the value is NULL,
// the number of bytes read and an error, in case the string is longer than // the number of bytes read and an error, in case the string is longer than
// the input slice // the input slice
func readLengthEncodedString(b []byte) ([]byte, bool, int, error) { func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
@ -485,32 +641,32 @@ func escapeBytesBackslash(buf, v []byte) []byte {
for _, c := range v { for _, c := range v {
switch c { switch c {
case '\x00': case '\x00':
buf[pos] = '\\'
buf[pos+1] = '0' buf[pos+1] = '0'
buf[pos] = '\\'
pos += 2 pos += 2
case '\n': case '\n':
buf[pos] = '\\'
buf[pos+1] = 'n' buf[pos+1] = 'n'
buf[pos] = '\\'
pos += 2 pos += 2
case '\r': case '\r':
buf[pos] = '\\'
buf[pos+1] = 'r' buf[pos+1] = 'r'
buf[pos] = '\\'
pos += 2 pos += 2
case '\x1a': case '\x1a':
buf[pos] = '\\'
buf[pos+1] = 'Z' buf[pos+1] = 'Z'
buf[pos] = '\\'
pos += 2 pos += 2
case '\'': case '\'':
buf[pos] = '\\'
buf[pos+1] = '\'' buf[pos+1] = '\''
buf[pos] = '\\'
pos += 2 pos += 2
case '"': case '"':
buf[pos] = '\\'
buf[pos+1] = '"' buf[pos+1] = '"'
buf[pos] = '\\'
pos += 2 pos += 2
case '\\': case '\\':
buf[pos] = '\\'
buf[pos+1] = '\\' buf[pos+1] = '\\'
buf[pos] = '\\'
pos += 2 pos += 2
default: default:
buf[pos] = c buf[pos] = c
@ -530,32 +686,32 @@ func escapeStringBackslash(buf []byte, v string) []byte {
c := v[i] c := v[i]
switch c { switch c {
case '\x00': case '\x00':
buf[pos] = '\\'
buf[pos+1] = '0' buf[pos+1] = '0'
buf[pos] = '\\'
pos += 2 pos += 2
case '\n': case '\n':
buf[pos] = '\\'
buf[pos+1] = 'n' buf[pos+1] = 'n'
buf[pos] = '\\'
pos += 2 pos += 2
case '\r': case '\r':
buf[pos] = '\\'
buf[pos+1] = 'r' buf[pos+1] = 'r'
buf[pos] = '\\'
pos += 2 pos += 2
case '\x1a': case '\x1a':
buf[pos] = '\\'
buf[pos+1] = 'Z' buf[pos+1] = 'Z'
buf[pos] = '\\'
pos += 2 pos += 2
case '\'': case '\'':
buf[pos] = '\\'
buf[pos+1] = '\'' buf[pos+1] = '\''
buf[pos] = '\\'
pos += 2 pos += 2
case '"': case '"':
buf[pos] = '\\'
buf[pos+1] = '"' buf[pos+1] = '"'
buf[pos] = '\\'
pos += 2 pos += 2
case '\\': case '\\':
buf[pos] = '\\'
buf[pos+1] = '\\' buf[pos+1] = '\\'
buf[pos] = '\\'
pos += 2 pos += 2
default: default:
buf[pos] = c buf[pos] = c
@ -577,8 +733,8 @@ func escapeBytesQuotes(buf, v []byte) []byte {
for _, c := range v { for _, c := range v {
if c == '\'' { if c == '\'' {
buf[pos] = '\''
buf[pos+1] = '\'' buf[pos+1] = '\''
buf[pos] = '\''
pos += 2 pos += 2
} else { } else {
buf[pos] = c buf[pos] = c
@ -597,8 +753,8 @@ func escapeStringQuotes(buf []byte, v string) []byte {
for i := 0; i < len(v); i++ { for i := 0; i < len(v); i++ {
c := v[i] c := v[i]
if c == '\'' { if c == '\'' {
buf[pos] = '\''
buf[pos+1] = '\'' buf[pos+1] = '\''
buf[pos] = '\''
pos += 2 pos += 2
} else { } else {
buf[pos] = c buf[pos] = c
@ -623,38 +779,15 @@ type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`. // Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {} func (*noCopy) Lock() {}
// atomicBool is a wrapper around uint32 for usage as a boolean value with // Unlock is a no-op used by -copylocks checker from `go vet`.
// atomic access. // noCopy should implement sync.Locker from Go 1.11
type atomicBool struct { // https://github.com/golang/go/commit/c2eba53e7f80df21d51285879d51ab81bcfbf6bc
_noCopy noCopy // https://github.com/golang/go/issues/26165
value uint32 func (*noCopy) Unlock() {}
}
// IsSet returns whether the current boolean value is true
func (ab *atomicBool) IsSet() bool {
return atomic.LoadUint32(&ab.value) > 0
}
// Set sets the value of the bool regardless of the previous value
func (ab *atomicBool) Set(value bool) {
if value {
atomic.StoreUint32(&ab.value, 1)
} else {
atomic.StoreUint32(&ab.value, 0)
}
}
// TrySet sets the value of the bool and returns whether the value changed
func (ab *atomicBool) TrySet(value bool) bool {
if value {
return atomic.SwapUint32(&ab.value, 1) == 0
}
return atomic.SwapUint32(&ab.value, 0) > 0
}
// atomicError is a wrapper for atomically accessed error values // atomicError is a wrapper for atomically accessed error values
type atomicError struct { type atomicError struct {
_noCopy noCopy _ noCopy
value atomic.Value value atomic.Value
} }

View File

@ -1,41 +1,5 @@
# GORM # GORM
The fantastic ORM library for Golang, aims to be developer friendly. GORM V2 moved to https://github.com/go-gorm/gorm
[![go report card](https://goreportcard.com/badge/github.com/jinzhu/gorm "go report card")](https://goreportcard.com/report/github.com/jinzhu/gorm) GORM V1 Doc https://v1.gorm.io/
[![wercker status](https://app.wercker.com/status/8596cace912c9947dd9c8542ecc8cb8b/s/master "wercker status")](https://app.wercker.com/project/byKey/8596cace912c9947dd9c8542ecc8cb8b)
[![codecov](https://codecov.io/gh/jinzhu/gorm/branch/master/graph/badge.svg)](https://codecov.io/gh/jinzhu/gorm)
[![Join the chat at https://gitter.im/jinzhu/gorm](https://img.shields.io/gitter/room/jinzhu/gorm.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Open Collective Backer](https://opencollective.com/gorm/tiers/backer/badge.svg?label=backer&color=brightgreen "Open Collective Backer")](https://opencollective.com/gorm)
[![Open Collective Sponsor](https://opencollective.com/gorm/tiers/sponsor/badge.svg?label=sponsor&color=brightgreen "Open Collective Sponsor")](https://opencollective.com/gorm)
[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
[![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm)
## Overview
* Full-Featured ORM (almost)
* Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
* Hooks (Before/After Create/Save/Update/Delete/Find)
* Preloading (eager loading)
* Transactions
* Composite Primary Key
* SQL Builder
* Auto Migrations
* Logger
* Extendable, write Plugins based on GORM callbacks
* Every feature comes with tests
* Developer Friendly
## Getting Started
* GORM Guides [https://gorm.io](https://gorm.io)
## Contributing
[You can help to deliver a better GORM, check out things you can do](https://gorm.io/contribute.html)
## License
© Jinzhu, 2013~time.Now
Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License)

15
vendor/github.com/jinzhu/gorm/go.mod generated vendored
View File

@ -1,15 +0,0 @@
module github.com/jinzhu/gorm
go 1.12
require (
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
github.com/go-sql-driver/mysql v1.4.1
github.com/jinzhu/inflection v1.0.0
github.com/jinzhu/now v1.0.1
github.com/lib/pq v1.1.1
github.com/mattn/go-sqlite3 v2.0.1+incompatible
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd // indirect
google.golang.org/appengine v1.4.0 // indirect
)

29
vendor/github.com/jinzhu/gorm/go.sum generated vendored
View File

@ -1,29 +0,0 @@
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View File

@ -531,6 +531,11 @@ func (s *DB) Debug() *DB {
// Transaction start a transaction as a block, // Transaction start a transaction as a block,
// return error will rollback, otherwise to commit. // return error will rollback, otherwise to commit.
func (s *DB) Transaction(fc func(tx *DB) error) (err error) { func (s *DB) Transaction(fc func(tx *DB) error) (err error) {
if _, ok := s.db.(*sql.Tx); ok {
return fc(s)
}
panicked := true panicked := true
tx := s.Begin() tx := s.Begin()
defer func() { defer func() {

View File

@ -152,6 +152,10 @@ func getForeignField(column string, fields []*StructField) *StructField {
// GetModelStruct get value's model struct, relationships based on struct and tag definition // GetModelStruct get value's model struct, relationships based on struct and tag definition
func (scope *Scope) GetModelStruct() *ModelStruct { func (scope *Scope) GetModelStruct() *ModelStruct {
return scope.getModelStruct(scope, make([]*StructField, 0))
}
func (scope *Scope) getModelStruct(rootScope *Scope, allFields []*StructField) *ModelStruct {
var modelStruct ModelStruct var modelStruct ModelStruct
// Scope value can't be nil // Scope value can't be nil
if scope.Value == nil { if scope.Value == nil {
@ -237,7 +241,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
field.IsNormal = true field.IsNormal = true
} else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous { } else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
// is embedded struct // is embedded struct
for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields { for _, subField := range scope.New(fieldValue).getModelStruct(rootScope, allFields).StructFields {
subField = subField.clone() subField = subField.clone()
subField.Names = append([]string{fieldStruct.Name}, subField.Names...) subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok { if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
@ -261,6 +265,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
modelStruct.StructFields = append(modelStruct.StructFields, subField) modelStruct.StructFields = append(modelStruct.StructFields, subField)
allFields = append(allFields, subField)
} }
continue continue
} else { } else {
@ -394,7 +399,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} else { } else {
// generate foreign keys from defined association foreign keys // generate foreign keys from defined association foreign keys
for _, scopeFieldName := range associationForeignKeys { for _, scopeFieldName := range associationForeignKeys {
if foreignField := getForeignField(scopeFieldName, modelStruct.StructFields); foreignField != nil { if foreignField := getForeignField(scopeFieldName, allFields); foreignField != nil {
foreignKeys = append(foreignKeys, associationType+foreignField.Name) foreignKeys = append(foreignKeys, associationType+foreignField.Name)
associationForeignKeys = append(associationForeignKeys, foreignField.Name) associationForeignKeys = append(associationForeignKeys, foreignField.Name)
} }
@ -406,13 +411,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
for _, foreignKey := range foreignKeys { for _, foreignKey := range foreignKeys {
if strings.HasPrefix(foreignKey, associationType) { if strings.HasPrefix(foreignKey, associationType) {
associationForeignKey := strings.TrimPrefix(foreignKey, associationType) associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { if foreignField := getForeignField(associationForeignKey, allFields); foreignField != nil {
associationForeignKeys = append(associationForeignKeys, associationForeignKey) associationForeignKeys = append(associationForeignKeys, associationForeignKey)
} }
} }
} }
if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 { if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
associationForeignKeys = []string{scope.PrimaryKey()} associationForeignKeys = []string{rootScope.PrimaryKey()}
} }
} else if len(foreignKeys) != len(associationForeignKeys) { } else if len(foreignKeys) != len(associationForeignKeys) {
scope.Err(errors.New("invalid foreign keys, should have same length")) scope.Err(errors.New("invalid foreign keys, should have same length"))
@ -422,7 +427,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
for idx, foreignKey := range foreignKeys { for idx, foreignKey := range foreignKeys {
if foreignField := getForeignField(foreignKey, toFields); foreignField != nil { if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
if associationField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); associationField != nil { if associationField := getForeignField(associationForeignKeys[idx], allFields); associationField != nil {
// mark field as foreignkey, use global lock to avoid race // mark field as foreignkey, use global lock to avoid race
structsLock.Lock() structsLock.Lock()
foreignField.IsForeignKey = true foreignField.IsForeignKey = true
@ -502,7 +507,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} else { } else {
// generate foreign keys form association foreign keys // generate foreign keys form association foreign keys
for _, associationForeignKey := range tagAssociationForeignKeys { for _, associationForeignKey := range tagAssociationForeignKeys {
if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { if foreignField := getForeignField(associationForeignKey, allFields); foreignField != nil {
foreignKeys = append(foreignKeys, associationType+foreignField.Name) foreignKeys = append(foreignKeys, associationType+foreignField.Name)
associationForeignKeys = append(associationForeignKeys, foreignField.Name) associationForeignKeys = append(associationForeignKeys, foreignField.Name)
} }
@ -514,13 +519,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
for _, foreignKey := range foreignKeys { for _, foreignKey := range foreignKeys {
if strings.HasPrefix(foreignKey, associationType) { if strings.HasPrefix(foreignKey, associationType) {
associationForeignKey := strings.TrimPrefix(foreignKey, associationType) associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { if foreignField := getForeignField(associationForeignKey, allFields); foreignField != nil {
associationForeignKeys = append(associationForeignKeys, associationForeignKey) associationForeignKeys = append(associationForeignKeys, associationForeignKey)
} }
} }
} }
if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 { if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
associationForeignKeys = []string{scope.PrimaryKey()} associationForeignKeys = []string{rootScope.PrimaryKey()}
} }
} else if len(foreignKeys) != len(associationForeignKeys) { } else if len(foreignKeys) != len(associationForeignKeys) {
scope.Err(errors.New("invalid foreign keys, should have same length")) scope.Err(errors.New("invalid foreign keys, should have same length"))
@ -530,7 +535,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
for idx, foreignKey := range foreignKeys { for idx, foreignKey := range foreignKeys {
if foreignField := getForeignField(foreignKey, toFields); foreignField != nil { if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
if scopeField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); scopeField != nil { if scopeField := getForeignField(associationForeignKeys[idx], allFields); scopeField != nil {
// mark field as foreignkey, use global lock to avoid race // mark field as foreignkey, use global lock to avoid race
structsLock.Lock() structsLock.Lock()
foreignField.IsForeignKey = true foreignField.IsForeignKey = true
@ -630,6 +635,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
modelStruct.StructFields = append(modelStruct.StructFields, field) modelStruct.StructFields = append(modelStruct.StructFields, field)
allFields = append(allFields, field)
} }
} }

View File

@ -590,7 +590,7 @@ func (scope *Scope) buildCondition(clause map[string]interface{}, include bool)
} }
scopeQuotedTableName := newScope.QuotedTableName() scopeQuotedTableName := newScope.QuotedTableName()
for _, field := range newScope.Fields() { for _, field := range newScope.Fields() {
if !field.IsIgnored && !field.IsBlank { if !field.IsIgnored && !field.IsBlank && field.Relationship == nil {
sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", scopeQuotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface()))) sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", scopeQuotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
} }
} }
@ -913,7 +913,8 @@ func (scope *Scope) updatedAttrsWithValues(value interface{}) (results map[strin
results = map[string]interface{}{} results = map[string]interface{}{}
for key, value := range convertInterfaceToMap(value, true, scope.db) { for key, value := range convertInterfaceToMap(value, true, scope.db) {
if field, ok := scope.FieldByName(key); ok && scope.changeableField(field) { if field, ok := scope.FieldByName(key); ok {
if scope.changeableField(field) {
if _, ok := value.(*SqlExpr); ok { if _, ok := value.(*SqlExpr); ok {
hasUpdate = true hasUpdate = true
results[field.DBName] = value results[field.DBName] = value
@ -929,6 +930,9 @@ func (scope *Scope) updatedAttrsWithValues(value interface{}) (results map[strin
} }
} }
} }
} else {
results[key] = value
}
} }
return return
} }

View File

@ -32,7 +32,57 @@ type searchPreload struct {
} }
func (s *search) clone() *search { func (s *search) clone() *search {
clone := *s clone := search{
db: s.db,
whereConditions: make([]map[string]interface{}, len(s.whereConditions)),
orConditions: make([]map[string]interface{}, len(s.orConditions)),
notConditions: make([]map[string]interface{}, len(s.notConditions)),
havingConditions: make([]map[string]interface{}, len(s.havingConditions)),
joinConditions: make([]map[string]interface{}, len(s.joinConditions)),
initAttrs: make([]interface{}, len(s.initAttrs)),
assignAttrs: make([]interface{}, len(s.assignAttrs)),
selects: s.selects,
omits: make([]string, len(s.omits)),
orders: make([]interface{}, len(s.orders)),
preload: make([]searchPreload, len(s.preload)),
offset: s.offset,
limit: s.limit,
group: s.group,
tableName: s.tableName,
raw: s.raw,
Unscoped: s.Unscoped,
ignoreOrderQuery: s.ignoreOrderQuery,
}
for i, value := range s.whereConditions {
clone.whereConditions[i] = value
}
for i, value := range s.orConditions {
clone.orConditions[i] = value
}
for i, value := range s.notConditions {
clone.notConditions[i] = value
}
for i, value := range s.havingConditions {
clone.havingConditions[i] = value
}
for i, value := range s.joinConditions {
clone.joinConditions[i] = value
}
for i, value := range s.initAttrs {
clone.initAttrs[i] = value
}
for i, value := range s.assignAttrs {
clone.assignAttrs[i] = value
}
for i, value := range s.omits {
clone.omits[i] = value
}
for i, value := range s.orders {
clone.orders[i] = value
}
for i, value := range s.preload {
clone.preload[i] = value
}
return &clone return &clone
} }

View File

@ -142,11 +142,6 @@ build:
code: | code: |
GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./... GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
- script:
name: test mssql
code: |
GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test -race ./...
- script: - script:
name: codecov name: codecov
code: | code: |

View File

@ -1 +0,0 @@
module github.com/jinzhu/inflection

View File

@ -6,6 +6,7 @@
# Folders # Folders
_obj _obj
_test _test
.idea
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]

View File

@ -18,9 +18,8 @@ before_install:
# go versions to test # go versions to test
go: go:
- "1.8" - "1.15.x"
- "1.9" - "1.16.x"
- "1.10.x"
# run tests w/ coverage # run tests w/ coverage
script: script:

View File

@ -15,30 +15,34 @@ Major additional concepts are:
* `Get` and `Select` to go quickly from query to struct/slice * `Get` and `Select` to go quickly from query to struct/slice
In addition to the [godoc API documentation](http://godoc.org/github.com/jmoiron/sqlx), In addition to the [godoc API documentation](http://godoc.org/github.com/jmoiron/sqlx),
there is also some [standard documentation](http://jmoiron.github.io/sqlx/) that there is also some [user documentation](http://jmoiron.github.io/sqlx/) that
explains how to use `database/sql` along with sqlx. explains how to use `database/sql` along with sqlx.
## Recent Changes ## Recent Changes
* The [introduction](https://github.com/jmoiron/sqlx/pull/387) of `sql.ColumnType` sets the required minimum Go version to 1.8. 1.3.0:
* sqlx/types.JsonText has been renamed to JSONText to follow Go naming conventions. * `sqlx.DB.Connx(context.Context) *sqlx.Conn`
* `sqlx.BindDriver(driverName, bindType)`
* support for `[]map[string]interface{}` to do "batch" insertions
* allocation & perf improvements for `sqlx.In`
This breaks backwards compatibility, but it's in a way that is trivially fixable DB.Connx returns an `sqlx.Conn`, which is an `sql.Conn`-alike consistent with
(`s/JsonText/JSONText/g`). The `types` package is both experimental and not in sqlx's wrapping of other types.
active development currently.
* Using Go 1.6 and below with `types.JSONText` and `types.GzippedText` can be _potentially unsafe_, **especially** when used with common auto-scan sqlx idioms like `Select` and `Get`. See [golang bug #13905](https://github.com/golang/go/issues/13905). `BindDriver` allows users to control the bindvars that sqlx will use for drivers,
and add new drivers at runtime. This results in a very slight performance hit
when resolving the driver into a bind type (~40ns per call), but it allows users
to specify what bindtype their driver uses even when sqlx has not been updated
to know about it by default.
### Backwards Compatibility ### Backwards Compatibility
There is no Go1-like promise of absolute stability, but I take the issue seriously Compatibility with the most recent two versions of Go is a requirement for any
and will maintain the library in a compatible state unless vital bugs prevent me new changes. Compatibility beyond that is not guaranteed.
from doing so. Since [#59](https://github.com/jmoiron/sqlx/issues/59) and
[#60](https://github.com/jmoiron/sqlx/issues/60) necessitated breaking behavior, Versioning is done with Go modules. Breaking changes (eg. removing deprecated API)
a wider API cleanup was done at the time of fixing. It's possible this will happen will get major version number bumps.
in future; if it does, a git tag will be provided for users requiring the old
behavior to continue to use it until such a time as they can migrate.
## install ## install
@ -102,7 +106,7 @@ type Place struct {
} }
func main() { func main() {
// this Pings the database trying to connect, panics on error // this Pings the database trying to connect
// use sqlx.Open() for sql.Open() semantics // use sqlx.Open() for sql.Open() semantics
db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable") db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
if err != nil { if err != nil {
@ -182,6 +186,28 @@ func main() {
// as the name -> db mapping, so struct fields are lowercased and the `db` tag // as the name -> db mapping, so struct fields are lowercased and the `db` tag
// is taken into consideration. // is taken into consideration.
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason) rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
// batch insert
// batch insert with structs
personStructs := []Person{
{FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
{FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
{FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
}
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personStructs)
// batch insert with maps
personMaps := []map[string]interface{}{
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
{"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
}
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personMaps)
} }
``` ```

View File

@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/jmoiron/sqlx/reflectx" "github.com/jmoiron/sqlx/reflectx"
) )
@ -20,21 +21,36 @@ const (
AT AT
) )
var defaultBinds = map[int][]string{
DOLLAR: []string{"postgres", "pgx", "pq-timeouts", "cloudsqlpostgres", "ql", "nrpostgres", "cockroach"},
QUESTION: []string{"mysql", "sqlite3", "nrmysql", "nrsqlite3"},
NAMED: []string{"oci8", "ora", "goracle", "godror"},
AT: []string{"sqlserver"},
}
var binds sync.Map
func init() {
for bind, drivers := range defaultBinds {
for _, driver := range drivers {
BindDriver(driver, bind)
}
}
}
// BindType returns the bindtype for a given database given a drivername. // BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int { func BindType(driverName string) int {
switch driverName { itype, ok := binds.Load(driverName)
case "postgres", "pgx", "pq-timeouts", "cloudsqlpostgres": if !ok {
return DOLLAR
case "mysql":
return QUESTION
case "sqlite3":
return QUESTION
case "oci8", "ora", "goracle":
return NAMED
case "sqlserver":
return AT
}
return UNKNOWN return UNKNOWN
}
return itype.(int)
}
// BindDriver sets the BindType for driverName to bindType.
func BindDriver(driverName string, bindType int) {
binds.Store(driverName, bindType)
} }
// FIXME: this should be able to be tolerant of escaped ?'s in queries without // FIXME: this should be able to be tolerant of escaped ?'s in queries without
@ -98,6 +114,28 @@ func rebindBuff(bindType int, query string) string {
return rqb.String() return rqb.String()
} }
func asSliceForIn(i interface{}) (v reflect.Value, ok bool) {
if i == nil {
return reflect.Value{}, false
}
v = reflect.ValueOf(i)
t := reflectx.Deref(v.Type())
// Only expand slices
if t.Kind() != reflect.Slice {
return reflect.Value{}, false
}
// []byte is a driver.Value type so it should not be expanded
if t == reflect.TypeOf([]byte{}) {
return reflect.Value{}, false
}
return v, true
}
// In expands slice values in args, returning the modified query string // In expands slice values in args, returning the modified query string
// and a new arg list that can be executed by a database. The `query` should // and a new arg list that can be executed by a database. The `query` should
// use the `?` bindVar. The return value uses the `?` bindVar. // use the `?` bindVar. The return value uses the `?` bindVar.
@ -113,17 +151,25 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
var flatArgsCount int var flatArgsCount int
var anySlices bool var anySlices bool
meta := make([]argMeta, len(args)) var stackMeta [32]argMeta
var meta []argMeta
if len(args) <= len(stackMeta) {
meta = stackMeta[:len(args)]
} else {
meta = make([]argMeta, len(args))
}
for i, arg := range args { for i, arg := range args {
if a, ok := arg.(driver.Valuer); ok { if a, ok := arg.(driver.Valuer); ok {
arg, _ = a.Value() var err error
arg, err = a.Value()
if err != nil {
return "", nil, err
}
} }
v := reflect.ValueOf(arg)
t := reflectx.Deref(v.Type())
// []byte is a driver.Value type so it should not be expanded if v, ok := asSliceForIn(arg); ok {
if t.Kind() == reflect.Slice && t != reflect.TypeOf([]byte{}) {
meta[i].length = v.Len() meta[i].length = v.Len()
meta[i].v = v meta[i].v = v
@ -146,7 +192,9 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
} }
newArgs := make([]interface{}, 0, flatArgsCount) newArgs := make([]interface{}, 0, flatArgsCount)
buf := make([]byte, 0, len(query)+len(", ?")*flatArgsCount)
var buf strings.Builder
buf.Grow(len(query) + len(", ?")*flatArgsCount)
var arg, offset int var arg, offset int
@ -172,10 +220,10 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
} }
// write everything up to and including our ? character // write everything up to and including our ? character
buf = append(buf, query[:offset+i+1]...) buf.WriteString(query[:offset+i+1])
for si := 1; si < argMeta.length; si++ { for si := 1; si < argMeta.length; si++ {
buf = append(buf, ", ?"...) buf.WriteString(", ?")
} }
newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length) newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
@ -186,13 +234,13 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
offset = 0 offset = 0
} }
buf = append(buf, query...) buf.WriteString(query)
if arg < len(meta) { if arg < len(meta) {
return "", nil, errors.New("number of bindVars less than number arguments") return "", nil, errors.New("number of bindVars less than number arguments")
} }
return string(buf), newArgs, nil return buf.String(), newArgs, nil
} }
func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} { func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {

View File

@ -1,7 +0,0 @@
module github.com/jmoiron/sqlx
require (
github.com/go-sql-driver/mysql v1.4.0
github.com/lib/pq v1.0.0
github.com/mattn/go-sqlite3 v1.9.0
)

View File

@ -1,6 +0,0 @@
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=

View File

@ -12,10 +12,12 @@ package sqlx
// * bindArgs, bindMapArgs, bindAnyArgs - given a list of names, return an arglist // * bindArgs, bindMapArgs, bindAnyArgs - given a list of names, return an arglist
// //
import ( import (
"bytes"
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"unicode" "unicode"
@ -144,8 +146,22 @@ func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
}, nil }, nil
} }
// convertMapStringInterface attempts to convert v to map[string]interface{}.
// Unlike v.(map[string]interface{}), this function works on named types that
// are convertible to map[string]interface{} as well.
func convertMapStringInterface(v interface{}) (map[string]interface{}, bool) {
var m map[string]interface{}
mtype := reflect.TypeOf(m)
t := reflect.TypeOf(v)
if !t.ConvertibleTo(mtype) {
return nil, false
}
return reflect.ValueOf(v).Convert(mtype).Interface().(map[string]interface{}), true
}
func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) { func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok { if maparg, ok := convertMapStringInterface(arg); ok {
return bindMapArgs(names, maparg) return bindMapArgs(names, maparg)
} }
return bindArgs(names, arg, m) return bindArgs(names, arg, m)
@ -200,7 +216,7 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
return "", []interface{}{}, err return "", []interface{}{}, err
} }
arglist, err := bindArgs(names, arg, m) arglist, err := bindAnyArgs(names, arg, m)
if err != nil { if err != nil {
return "", []interface{}{}, err return "", []interface{}{}, err
} }
@ -208,6 +224,82 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
return bound, arglist, nil return bound, arglist, nil
} }
var valuesReg = regexp.MustCompile(`\)\s*(?i)VALUES\s*\(`)
func findMatchingClosingBracketIndex(s string) int {
count := 0
for i, ch := range s {
if ch == '(' {
count++
}
if ch == ')' {
count--
if count == 0 {
return i
}
}
}
return 0
}
func fixBound(bound string, loop int) string {
loc := valuesReg.FindStringIndex(bound)
// defensive guard when "VALUES (...)" not found
if len(loc) < 2 {
return bound
}
openingBracketIndex := loc[1] - 1
index := findMatchingClosingBracketIndex(bound[openingBracketIndex:])
// defensive guard. must have closing bracket
if index == 0 {
return bound
}
closingBracketIndex := openingBracketIndex + index + 1
var buffer bytes.Buffer
buffer.WriteString(bound[0:closingBracketIndex])
for i := 0; i < loop-1; i++ {
buffer.WriteString(",")
buffer.WriteString(bound[openingBracketIndex:closingBracketIndex])
}
buffer.WriteString(bound[closingBracketIndex:])
return buffer.String()
}
// bindArray binds a named parameter query with fields from an array or slice of
// structs argument.
func bindArray(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
// do the initial binding with QUESTION; if bindType is not question,
// we can rebind it at the end.
bound, names, err := compileNamedQuery([]byte(query), QUESTION)
if err != nil {
return "", []interface{}{}, err
}
arrayValue := reflect.ValueOf(arg)
arrayLen := arrayValue.Len()
if arrayLen == 0 {
return "", []interface{}{}, fmt.Errorf("length of array is 0: %#v", arg)
}
var arglist = make([]interface{}, 0, len(names)*arrayLen)
for i := 0; i < arrayLen; i++ {
elemArglist, err := bindAnyArgs(names, arrayValue.Index(i).Interface(), m)
if err != nil {
return "", []interface{}{}, err
}
arglist = append(arglist, elemArglist...)
}
if arrayLen > 1 {
bound = fixBound(bound, arrayLen)
}
// adjust binding type if we weren't on question
if bindType != QUESTION {
bound = Rebind(bindType, bound)
}
return bound, arglist, nil
}
// bindMap binds a named parameter query with a map of arguments. // bindMap binds a named parameter query with a map of arguments.
func bindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) { func bindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) {
bound, names, err := compileNamedQuery([]byte(query), bindType) bound, names, err := compileNamedQuery([]byte(query), bindType)
@ -259,7 +351,7 @@ func compileNamedQuery(qs []byte, bindType int) (query string, names []string, e
} }
inName = true inName = true
name = []byte{} name = []byte{}
} else if inName && i > 0 && b == '=' { } else if inName && i > 0 && b == '=' && len(name) == 0 {
rebound = append(rebound, ':', '=') rebound = append(rebound, ':', '=')
inName = false inName = false
continue continue
@ -327,10 +419,20 @@ func Named(query string, arg interface{}) (string, []interface{}, error) {
} }
func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) { func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok { t := reflect.TypeOf(arg)
return bindMap(bindType, query, maparg) k := t.Kind()
switch {
case k == reflect.Map && t.Key().Kind() == reflect.String:
m, ok := convertMapStringInterface(arg)
if !ok {
return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg)
} }
return bindMap(bindType, query, m)
case k == reflect.Array || k == reflect.Slice:
return bindArray(bindType, query, arg, m)
default:
return bindStruct(bindType, query, arg, m) return bindStruct(bindType, query, arg, m)
}
} }
// NamedQuery binds a named query and then runs Query on the result using the // NamedQuery binds a named query and then runs Query on the result using the
@ -346,7 +448,7 @@ func NamedQuery(e Ext, query string, arg interface{}) (*Rows, error) {
// NamedExec uses BindStruct to get a query executable by the driver and // NamedExec uses BindStruct to get a query executable by the driver and
// then runs Exec on the result. Returns an error from the binding // then runs Exec on the result. Returns an error from the binding
// or the query excution itself. // or the query execution itself.
func NamedExec(e Ext, query string, arg interface{}) (sql.Result, error) { func NamedExec(e Ext, query string, arg interface{}) (sql.Result, error) {
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e)) q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
if err != nil { if err != nil {

View File

@ -122,7 +122,7 @@ func NamedQueryContext(ctx context.Context, e ExtContext, query string, arg inte
// NamedExecContext uses BindStruct to get a query executable by the driver and // NamedExecContext uses BindStruct to get a query executable by the driver and
// then runs Exec on the result. Returns an error from the binding // then runs Exec on the result. Returns an error from the binding
// or the query excution itself. // or the query execution itself.
func NamedExecContext(ctx context.Context, e ExtContext, query string, arg interface{}) (sql.Result, error) { func NamedExecContext(ctx context.Context, e ExtContext, query string, arg interface{}) (sql.Result, error) {
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e)) q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
if err != nil { if err != nil {

View File

@ -269,9 +269,7 @@ type typeQueue struct {
// A copying append that creates a new slice each time. // A copying append that creates a new slice each time.
func apnd(is []int, i int) []int { func apnd(is []int, i int) []int {
x := make([]int, len(is)+1) x := make([]int, len(is)+1)
for p, n := range is { copy(x, is)
x[p] = n
}
x[len(x)-1] = i x[len(x)-1] = i
return x return x
} }
@ -431,11 +429,16 @@ QueueLoop:
flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}} flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}}
for _, fi := range flds.Index { for _, fi := range flds.Index {
// check if nothing has already been pushed with the same path
// sometimes you can choose to override a type using embedded struct
fld, ok := flds.Paths[fi.Path]
if !ok || fld.Embedded {
flds.Paths[fi.Path] = fi flds.Paths[fi.Path] = fi
if fi.Name != "" && !fi.Embedded { if fi.Name != "" && !fi.Embedded {
flds.Names[fi.Path] = fi flds.Names[fi.Path] = fi
} }
} }
}
return flds return flds
} }

View File

@ -64,11 +64,7 @@ func isScannable(t reflect.Type) bool {
// it's not important that we use the right mapper for this particular object, // it's not important that we use the right mapper for this particular object,
// we're only concerned on how many exported fields this struct has // we're only concerned on how many exported fields this struct has
m := mapper() return len(mapper().TypeMap(t).Index) == 0
if len(m.TypeMap(t).Index) == 0 {
return true
}
return false
} }
// ColScanner is an interface used by MapScan and SliceScan // ColScanner is an interface used by MapScan and SliceScan
@ -149,15 +145,15 @@ func isUnsafe(i interface{}) bool {
} }
func mapperFor(i interface{}) *reflectx.Mapper { func mapperFor(i interface{}) *reflectx.Mapper {
switch i.(type) { switch i := i.(type) {
case DB: case DB:
return i.(DB).Mapper return i.Mapper
case *DB: case *DB:
return i.(*DB).Mapper return i.Mapper
case Tx: case Tx:
return i.(Tx).Mapper return i.Mapper
case *Tx: case *Tx:
return i.(*Tx).Mapper return i.Mapper
default: default:
return mapper() return mapper()
} }
@ -380,6 +376,14 @@ func (db *DB) PrepareNamed(query string) (*NamedStmt, error) {
return prepareNamed(db, query) return prepareNamed(db, query)
} }
// Conn is a wrapper around sql.Conn with extra functionality
type Conn struct {
*sql.Conn
driverName string
unsafe bool
Mapper *reflectx.Mapper
}
// Tx is an sqlx wrapper around sql.Tx with extra functionality // Tx is an sqlx wrapper around sql.Tx with extra functionality
type Tx struct { type Tx struct {
*sql.Tx *sql.Tx
@ -874,9 +878,10 @@ func structOnlyError(t reflect.Type) error {
} }
// scanAll scans all rows into a destination, which must be a slice of any // scanAll scans all rows into a destination, which must be a slice of any
// type. If the destination slice type is a Struct, then StructScan will be // type. It resets the slice length to zero before appending each element to
// used on each row. If the destination is some other kind of base type, then // the slice. If the destination slice type is a Struct, then StructScan will
// each row must only have one column which can scan into that type. This // be used on each row. If the destination is some other kind of base type,
// then each row must only have one column which can scan into that type. This
// allows you to do something like: // allows you to do something like:
// //
// rows, _ := db.Query("select id from people;") // rows, _ := db.Query("select id from people;")
@ -906,6 +911,7 @@ func scanAll(rows rowsi, dest interface{}, structOnly bool) error {
if err != nil { if err != nil {
return err return err
} }
direct.SetLen(0)
isPtr := slice.Elem().Kind() == reflect.Ptr isPtr := slice.Elem().Kind() == reflect.Ptr
base := reflectx.Deref(slice.Elem()) base := reflectx.Deref(slice.Elem())

View File

@ -208,6 +208,74 @@ func (db *DB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err
} }
// Connx returns an *sqlx.Conn instead of an *sql.Conn.
func (db *DB) Connx(ctx context.Context) (*Conn, error) {
conn, err := db.DB.Conn(ctx)
if err != nil {
return nil, err
}
return &Conn{Conn: conn, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, nil
}
// BeginTxx begins a transaction and returns an *sqlx.Tx instead of an
// *sql.Tx.
//
// The provided context is used until the transaction is committed or rolled
// back. If the context is canceled, the sql package will roll back the
// transaction. Tx.Commit will return an error if the context provided to
// BeginxContext is canceled.
func (c *Conn) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
tx, err := c.Conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
return &Tx{Tx: tx, driverName: c.driverName, unsafe: c.unsafe, Mapper: c.Mapper}, err
}
// SelectContext using this Conn.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
return SelectContext(ctx, c, dest, query, args...)
}
// GetContext using this Conn.
// Any placeholder parameters are replaced with supplied args.
// An error is returned if the result set is empty.
func (c *Conn) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
return GetContext(ctx, c, dest, query, args...)
}
// PreparexContext returns an sqlx.Stmt instead of a sql.Stmt.
//
// The provided context is used for the preparation of the statement, not for
// the execution of the statement.
func (c *Conn) PreparexContext(ctx context.Context, query string) (*Stmt, error) {
return PreparexContext(ctx, c, query)
}
// QueryxContext queries the database and returns an *sqlx.Rows.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
r, err := c.Conn.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
return &Rows{Rows: r, unsafe: c.unsafe, Mapper: c.Mapper}, err
}
// QueryRowxContext queries the database and returns an *sqlx.Row.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row {
rows, err := c.Conn.QueryContext(ctx, query, args...)
return &Row{rows: rows, err: err, unsafe: c.unsafe, Mapper: c.Mapper}
}
// Rebind a query within a Conn's bindvar type.
func (c *Conn) Rebind(query string) string {
return Rebind(BindType(c.driverName), query)
}
// StmtxContext returns a version of the prepared statement which runs within a // StmtxContext returns a version of the prepared statement which runs within a
// transaction. Provided stmt can be either *sql.Stmt or *sqlx.Stmt. // transaction. Provided stmt can be either *sql.Stmt or *sqlx.Stmt.
func (tx *Tx) StmtxContext(ctx context.Context, stmt interface{}) *Stmt { func (tx *Tx) StmtxContext(ctx context.Context, stmt interface{}) *Stmt {

27
vendor/golang.org/x/crypto/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/crypto/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

71
vendor/golang.org/x/crypto/ed25519/ed25519.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
//
// Beginning with Go 1.13, the functionality of this package was moved to the
// standard library as crypto/ed25519. This package only acts as a compatibility
// wrapper.
package ed25519
import (
"crypto/ed25519"
"io"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
)
// PublicKey is the type of Ed25519 public keys.
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return ed25519.NewKeyFromSeed(seed)
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
}

62
vendor/golang.org/x/crypto/sha3/doc.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sha3 implements the SHA-3 fixed-output-length hash functions and
// the SHAKE variable-output-length hash functions defined by FIPS-202.
//
// Both types of hash function use the "sponge" construction and the Keccak
// permutation. For a detailed specification see http://keccak.noekeon.org/
//
// # Guidance
//
// If you aren't sure what function you need, use SHAKE256 with at least 64
// bytes of output. The SHAKE instances are faster than the SHA3 instances;
// the latter have to allocate memory to conform to the hash.Hash interface.
//
// If you need a secret-key MAC (message authentication code), prepend the
// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
// output.
//
// # Security strengths
//
// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
// strength against preimage attacks of x bits. Since they only produce "x"
// bits of output, their collision-resistance is only "x/2" bits.
//
// The SHAKE-256 and -128 functions have a generic security strength of 256 and
// 128 bits against all attacks, provided that at least 2x bits of their output
// is used. Requesting more than 64 or 32 bytes of output, respectively, does
// not increase the collision-resistance of the SHAKE functions.
//
// # The sponge construction
//
// A sponge builds a pseudo-random function from a public pseudo-random
// permutation, by applying the permutation to a state of "rate + capacity"
// bytes, but hiding "capacity" of the bytes.
//
// A sponge starts out with a zero state. To hash an input using a sponge, up
// to "rate" bytes of the input are XORed into the sponge's state. The sponge
// is then "full" and the permutation is applied to "empty" it. This process is
// repeated until all the input has been "absorbed". The input is then padded.
// The digest is "squeezed" from the sponge in the same way, except that output
// is copied out instead of input being XORed in.
//
// A sponge is parameterized by its generic security strength, which is equal
// to half its capacity; capacity + rate is equal to the permutation's width.
// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
//
// # Recommendations
//
// The SHAKE functions are recommended for most new uses. They can produce
// output of arbitrary length. SHAKE256, with an output length of at least
// 64 bytes, provides 256-bit security against all attacks. The Keccak team
// recommends it for most applications upgrading from SHA2-512. (NIST chose a
// much stronger, but much slower, sponge instance for SHA3-512.)
//
// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions.
// They produce output of the same length, with the same security strengths
// against all attacks. This means, in particular, that SHA3-256 only has
// 128-bit collision resistance, because its output length is 32 bytes.
package sha3 // import "golang.org/x/crypto/sha3"

97
vendor/golang.org/x/crypto/sha3/hashes.go generated vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
// This file provides functions for creating instances of the SHA-3
// and SHAKE hash functions, as well as utility functions for hashing
// bytes.
import (
"hash"
)
// New224 creates a new SHA3-224 hash.
// Its generic security strength is 224 bits against preimage attacks,
// and 112 bits against collision attacks.
func New224() hash.Hash {
if h := new224Asm(); h != nil {
return h
}
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
}
// New256 creates a new SHA3-256 hash.
// Its generic security strength is 256 bits against preimage attacks,
// and 128 bits against collision attacks.
func New256() hash.Hash {
if h := new256Asm(); h != nil {
return h
}
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
}
// New384 creates a new SHA3-384 hash.
// Its generic security strength is 384 bits against preimage attacks,
// and 192 bits against collision attacks.
func New384() hash.Hash {
if h := new384Asm(); h != nil {
return h
}
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
}
// New512 creates a new SHA3-512 hash.
// Its generic security strength is 512 bits against preimage attacks,
// and 256 bits against collision attacks.
func New512() hash.Hash {
if h := new512Asm(); h != nil {
return h
}
return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
}
// NewLegacyKeccak256 creates a new Keccak-256 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New256 instead.
func NewLegacyKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
// NewLegacyKeccak512 creates a new Keccak-512 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New512 instead.
func NewLegacyKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} }
// Sum224 returns the SHA3-224 digest of the data.
func Sum224(data []byte) (digest [28]byte) {
h := New224()
h.Write(data)
h.Sum(digest[:0])
return
}
// Sum256 returns the SHA3-256 digest of the data.
func Sum256(data []byte) (digest [32]byte) {
h := New256()
h.Write(data)
h.Sum(digest[:0])
return
}
// Sum384 returns the SHA3-384 digest of the data.
func Sum384(data []byte) (digest [48]byte) {
h := New384()
h.Write(data)
h.Sum(digest[:0])
return
}
// Sum512 returns the SHA3-512 digest of the data.
func Sum512(data []byte) (digest [64]byte) {
h := New512()
h.Write(data)
h.Sum(digest[:0])
return
}

28
vendor/golang.org/x/crypto/sha3/hashes_generic.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
// +build !gc purego !s390x
package sha3
import (
"hash"
)
// new224Asm returns an assembly implementation of SHA3-224 if available,
// otherwise it returns nil.
func new224Asm() hash.Hash { return nil }
// new256Asm returns an assembly implementation of SHA3-256 if available,
// otherwise it returns nil.
func new256Asm() hash.Hash { return nil }
// new384Asm returns an assembly implementation of SHA3-384 if available,
// otherwise it returns nil.
func new384Asm() hash.Hash { return nil }
// new512Asm returns an assembly implementation of SHA3-512 if available,
// otherwise it returns nil.
func new512Asm() hash.Hash { return nil }

415
vendor/golang.org/x/crypto/sha3/keccakf.go generated vendored Normal file
View File

@ -0,0 +1,415 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
// +build !amd64 purego !gc
package sha3
import "math/bits"
// rc stores the round constants for use in the ι step.
var rc = [24]uint64{
0x0000000000000001,
0x0000000000008082,
0x800000000000808A,
0x8000000080008000,
0x000000000000808B,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008A,
0x0000000000000088,
0x0000000080008009,
0x000000008000000A,
0x000000008000808B,
0x800000000000008B,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800A,
0x800000008000000A,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
}
// keccakF1600 applies the Keccak permutation to a 1600b-wide
// state represented as a slice of 25 uint64s.
func keccakF1600(a *[25]uint64) {
// Implementation translated from Keccak-inplace.c
// in the keccak reference code.
var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64
for i := 0; i < 24; i += 4 {
// Combines the 5 steps in each round into 2 steps.
// Unrolls 4 rounds per loop and spreads some steps across rounds.
// Round 1
bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
d0 = bc4 ^ (bc1<<1 | bc1>>63)
d1 = bc0 ^ (bc2<<1 | bc2>>63)
d2 = bc1 ^ (bc3<<1 | bc3>>63)
d3 = bc2 ^ (bc4<<1 | bc4>>63)
d4 = bc3 ^ (bc0<<1 | bc0>>63)
bc0 = a[0] ^ d0
t = a[6] ^ d1
bc1 = bits.RotateLeft64(t, 44)
t = a[12] ^ d2
bc2 = bits.RotateLeft64(t, 43)
t = a[18] ^ d3
bc3 = bits.RotateLeft64(t, 21)
t = a[24] ^ d4
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i]
a[6] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
a[18] = bc3 ^ (bc0 &^ bc4)
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc2 = bits.RotateLeft64(t, 3)
t = a[16] ^ d1
bc3 = bits.RotateLeft64(t, 45)
t = a[22] ^ d2
bc4 = bits.RotateLeft64(t, 61)
t = a[3] ^ d3
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = bits.RotateLeft64(t, 20)
a[10] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
a[3] = bc3 ^ (bc0 &^ bc4)
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc4 = bits.RotateLeft64(t, 18)
t = a[1] ^ d1
bc0 = bits.RotateLeft64(t, 1)
t = a[7] ^ d2
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = bits.RotateLeft64(t, 25)
t = a[19] ^ d4
bc3 = bits.RotateLeft64(t, 8)
a[20] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
a[13] = bc3 ^ (bc0 &^ bc4)
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc1 = bits.RotateLeft64(t, 36)
t = a[11] ^ d1
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = bits.RotateLeft64(t, 15)
t = a[23] ^ d3
bc4 = bits.RotateLeft64(t, 56)
t = a[4] ^ d4
bc0 = bits.RotateLeft64(t, 27)
a[5] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
a[23] = bc3 ^ (bc0 &^ bc4)
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = bits.RotateLeft64(t, 2)
t = a[2] ^ d2
bc0 = bits.RotateLeft64(t, 62)
t = a[8] ^ d3
bc1 = bits.RotateLeft64(t, 55)
t = a[14] ^ d4
bc2 = bits.RotateLeft64(t, 39)
a[15] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
a[8] = bc3 ^ (bc0 &^ bc4)
a[14] = bc4 ^ (bc1 &^ bc0)
// Round 2
bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
d0 = bc4 ^ (bc1<<1 | bc1>>63)
d1 = bc0 ^ (bc2<<1 | bc2>>63)
d2 = bc1 ^ (bc3<<1 | bc3>>63)
d3 = bc2 ^ (bc4<<1 | bc4>>63)
d4 = bc3 ^ (bc0<<1 | bc0>>63)
bc0 = a[0] ^ d0
t = a[16] ^ d1
bc1 = bits.RotateLeft64(t, 44)
t = a[7] ^ d2
bc2 = bits.RotateLeft64(t, 43)
t = a[23] ^ d3
bc3 = bits.RotateLeft64(t, 21)
t = a[14] ^ d4
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1]
a[16] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
a[23] = bc3 ^ (bc0 &^ bc4)
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc2 = bits.RotateLeft64(t, 3)
t = a[11] ^ d1
bc3 = bits.RotateLeft64(t, 45)
t = a[2] ^ d2
bc4 = bits.RotateLeft64(t, 61)
t = a[18] ^ d3
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = bits.RotateLeft64(t, 20)
a[20] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
a[18] = bc3 ^ (bc0 &^ bc4)
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc4 = bits.RotateLeft64(t, 18)
t = a[6] ^ d1
bc0 = bits.RotateLeft64(t, 1)
t = a[22] ^ d2
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = bits.RotateLeft64(t, 25)
t = a[4] ^ d4
bc3 = bits.RotateLeft64(t, 8)
a[15] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
a[13] = bc3 ^ (bc0 &^ bc4)
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc1 = bits.RotateLeft64(t, 36)
t = a[1] ^ d1
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = bits.RotateLeft64(t, 15)
t = a[8] ^ d3
bc4 = bits.RotateLeft64(t, 56)
t = a[24] ^ d4
bc0 = bits.RotateLeft64(t, 27)
a[10] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
a[8] = bc3 ^ (bc0 &^ bc4)
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = bits.RotateLeft64(t, 2)
t = a[12] ^ d2
bc0 = bits.RotateLeft64(t, 62)
t = a[3] ^ d3
bc1 = bits.RotateLeft64(t, 55)
t = a[19] ^ d4
bc2 = bits.RotateLeft64(t, 39)
a[5] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
a[3] = bc3 ^ (bc0 &^ bc4)
a[19] = bc4 ^ (bc1 &^ bc0)
// Round 3
bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
d0 = bc4 ^ (bc1<<1 | bc1>>63)
d1 = bc0 ^ (bc2<<1 | bc2>>63)
d2 = bc1 ^ (bc3<<1 | bc3>>63)
d3 = bc2 ^ (bc4<<1 | bc4>>63)
d4 = bc3 ^ (bc0<<1 | bc0>>63)
bc0 = a[0] ^ d0
t = a[11] ^ d1
bc1 = bits.RotateLeft64(t, 44)
t = a[22] ^ d2
bc2 = bits.RotateLeft64(t, 43)
t = a[8] ^ d3
bc3 = bits.RotateLeft64(t, 21)
t = a[19] ^ d4
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2]
a[11] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
a[8] = bc3 ^ (bc0 &^ bc4)
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc2 = bits.RotateLeft64(t, 3)
t = a[1] ^ d1
bc3 = bits.RotateLeft64(t, 45)
t = a[12] ^ d2
bc4 = bits.RotateLeft64(t, 61)
t = a[23] ^ d3
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = bits.RotateLeft64(t, 20)
a[15] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
a[23] = bc3 ^ (bc0 &^ bc4)
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc4 = bits.RotateLeft64(t, 18)
t = a[16] ^ d1
bc0 = bits.RotateLeft64(t, 1)
t = a[2] ^ d2
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = bits.RotateLeft64(t, 25)
t = a[24] ^ d4
bc3 = bits.RotateLeft64(t, 8)
a[5] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
a[13] = bc3 ^ (bc0 &^ bc4)
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc1 = bits.RotateLeft64(t, 36)
t = a[6] ^ d1
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = bits.RotateLeft64(t, 15)
t = a[3] ^ d3
bc4 = bits.RotateLeft64(t, 56)
t = a[14] ^ d4
bc0 = bits.RotateLeft64(t, 27)
a[20] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
a[3] = bc3 ^ (bc0 &^ bc4)
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = bits.RotateLeft64(t, 2)
t = a[7] ^ d2
bc0 = bits.RotateLeft64(t, 62)
t = a[18] ^ d3
bc1 = bits.RotateLeft64(t, 55)
t = a[4] ^ d4
bc2 = bits.RotateLeft64(t, 39)
a[10] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
a[18] = bc3 ^ (bc0 &^ bc4)
a[4] = bc4 ^ (bc1 &^ bc0)
// Round 4
bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
d0 = bc4 ^ (bc1<<1 | bc1>>63)
d1 = bc0 ^ (bc2<<1 | bc2>>63)
d2 = bc1 ^ (bc3<<1 | bc3>>63)
d3 = bc2 ^ (bc4<<1 | bc4>>63)
d4 = bc3 ^ (bc0<<1 | bc0>>63)
bc0 = a[0] ^ d0
t = a[1] ^ d1
bc1 = bits.RotateLeft64(t, 44)
t = a[2] ^ d2
bc2 = bits.RotateLeft64(t, 43)
t = a[3] ^ d3
bc3 = bits.RotateLeft64(t, 21)
t = a[4] ^ d4
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3]
a[1] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
a[3] = bc3 ^ (bc0 &^ bc4)
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc2 = bits.RotateLeft64(t, 3)
t = a[6] ^ d1
bc3 = bits.RotateLeft64(t, 45)
t = a[7] ^ d2
bc4 = bits.RotateLeft64(t, 61)
t = a[8] ^ d3
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = bits.RotateLeft64(t, 20)
a[5] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
a[8] = bc3 ^ (bc0 &^ bc4)
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc4 = bits.RotateLeft64(t, 18)
t = a[11] ^ d1
bc0 = bits.RotateLeft64(t, 1)
t = a[12] ^ d2
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = bits.RotateLeft64(t, 25)
t = a[14] ^ d4
bc3 = bits.RotateLeft64(t, 8)
a[10] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
a[13] = bc3 ^ (bc0 &^ bc4)
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc1 = bits.RotateLeft64(t, 36)
t = a[16] ^ d1
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = bits.RotateLeft64(t, 15)
t = a[18] ^ d3
bc4 = bits.RotateLeft64(t, 56)
t = a[19] ^ d4
bc0 = bits.RotateLeft64(t, 27)
a[15] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
a[18] = bc3 ^ (bc0 &^ bc4)
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = bits.RotateLeft64(t, 2)
t = a[22] ^ d2
bc0 = bits.RotateLeft64(t, 62)
t = a[23] ^ d3
bc1 = bits.RotateLeft64(t, 55)
t = a[24] ^ d4
bc2 = bits.RotateLeft64(t, 39)
a[20] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
a[23] = bc3 ^ (bc0 &^ bc4)
a[24] = bc4 ^ (bc1 &^ bc0)
}
}

14
vendor/golang.org/x/crypto/sha3/keccakf_amd64.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && !purego && gc
// +build amd64,!purego,gc
package sha3
// This function is implemented in keccakf_amd64.s.
//go:noescape
func keccakF1600(a *[25]uint64)

391
vendor/golang.org/x/crypto/sha3/keccakf_amd64.s generated vendored Normal file
View File

@ -0,0 +1,391 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && !purego && gc
// +build amd64,!purego,gc
// This code was translated into a form compatible with 6a from the public
// domain sources at https://github.com/gvanas/KeccakCodePackage
// Offsets in state
#define _ba (0*8)
#define _be (1*8)
#define _bi (2*8)
#define _bo (3*8)
#define _bu (4*8)
#define _ga (5*8)
#define _ge (6*8)
#define _gi (7*8)
#define _go (8*8)
#define _gu (9*8)
#define _ka (10*8)
#define _ke (11*8)
#define _ki (12*8)
#define _ko (13*8)
#define _ku (14*8)
#define _ma (15*8)
#define _me (16*8)
#define _mi (17*8)
#define _mo (18*8)
#define _mu (19*8)
#define _sa (20*8)
#define _se (21*8)
#define _si (22*8)
#define _so (23*8)
#define _su (24*8)
// Temporary registers
#define rT1 AX
// Round vars
#define rpState DI
#define rpStack SP
#define rDa BX
#define rDe CX
#define rDi DX
#define rDo R8
#define rDu R9
#define rBa R10
#define rBe R11
#define rBi R12
#define rBo R13
#define rBu R14
#define rCa SI
#define rCe BP
#define rCi rBi
#define rCo rBo
#define rCu R15
#define MOVQ_RBI_RCE MOVQ rBi, rCe
#define XORQ_RT1_RCA XORQ rT1, rCa
#define XORQ_RT1_RCE XORQ rT1, rCe
#define XORQ_RBA_RCU XORQ rBa, rCu
#define XORQ_RBE_RCU XORQ rBe, rCu
#define XORQ_RDU_RCU XORQ rDu, rCu
#define XORQ_RDA_RCA XORQ rDa, rCa
#define XORQ_RDE_RCE XORQ rDe, rCe
#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \
/* Prepare round */ \
MOVQ rCe, rDa; \
ROLQ $1, rDa; \
\
MOVQ _bi(iState), rCi; \
XORQ _gi(iState), rDi; \
XORQ rCu, rDa; \
XORQ _ki(iState), rCi; \
XORQ _mi(iState), rDi; \
XORQ rDi, rCi; \
\
MOVQ rCi, rDe; \
ROLQ $1, rDe; \
\
MOVQ _bo(iState), rCo; \
XORQ _go(iState), rDo; \
XORQ rCa, rDe; \
XORQ _ko(iState), rCo; \
XORQ _mo(iState), rDo; \
XORQ rDo, rCo; \
\
MOVQ rCo, rDi; \
ROLQ $1, rDi; \
\
MOVQ rCu, rDo; \
XORQ rCe, rDi; \
ROLQ $1, rDo; \
\
MOVQ rCa, rDu; \
XORQ rCi, rDo; \
ROLQ $1, rDu; \
\
/* Result b */ \
MOVQ _ba(iState), rBa; \
MOVQ _ge(iState), rBe; \
XORQ rCo, rDu; \
MOVQ _ki(iState), rBi; \
MOVQ _mo(iState), rBo; \
MOVQ _su(iState), rBu; \
XORQ rDe, rBe; \
ROLQ $44, rBe; \
XORQ rDi, rBi; \
XORQ rDa, rBa; \
ROLQ $43, rBi; \
\
MOVQ rBe, rCa; \
MOVQ rc, rT1; \
ORQ rBi, rCa; \
XORQ rBa, rT1; \
XORQ rT1, rCa; \
MOVQ rCa, _ba(oState); \
\
XORQ rDu, rBu; \
ROLQ $14, rBu; \
MOVQ rBa, rCu; \
ANDQ rBe, rCu; \
XORQ rBu, rCu; \
MOVQ rCu, _bu(oState); \
\
XORQ rDo, rBo; \
ROLQ $21, rBo; \
MOVQ rBo, rT1; \
ANDQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _bi(oState); \
\
NOTQ rBi; \
ORQ rBa, rBu; \
ORQ rBo, rBi; \
XORQ rBo, rBu; \
XORQ rBe, rBi; \
MOVQ rBu, _bo(oState); \
MOVQ rBi, _be(oState); \
B_RBI_RCE; \
\
/* Result g */ \
MOVQ _gu(iState), rBe; \
XORQ rDu, rBe; \
MOVQ _ka(iState), rBi; \
ROLQ $20, rBe; \
XORQ rDa, rBi; \
ROLQ $3, rBi; \
MOVQ _bo(iState), rBa; \
MOVQ rBe, rT1; \
ORQ rBi, rT1; \
XORQ rDo, rBa; \
MOVQ _me(iState), rBo; \
MOVQ _si(iState), rBu; \
ROLQ $28, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ga(oState); \
G_RT1_RCA; \
\
XORQ rDe, rBo; \
ROLQ $45, rBo; \
MOVQ rBi, rT1; \
ANDQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _ge(oState); \
G_RT1_RCE; \
\
XORQ rDi, rBu; \
ROLQ $61, rBu; \
MOVQ rBu, rT1; \
ORQ rBa, rT1; \
XORQ rBo, rT1; \
MOVQ rT1, _go(oState); \
\
ANDQ rBe, rBa; \
XORQ rBu, rBa; \
MOVQ rBa, _gu(oState); \
NOTQ rBu; \
G_RBA_RCU; \
\
ORQ rBu, rBo; \
XORQ rBi, rBo; \
MOVQ rBo, _gi(oState); \
\
/* Result k */ \
MOVQ _be(iState), rBa; \
MOVQ _gi(iState), rBe; \
MOVQ _ko(iState), rBi; \
MOVQ _mu(iState), rBo; \
MOVQ _sa(iState), rBu; \
XORQ rDi, rBe; \
ROLQ $6, rBe; \
XORQ rDo, rBi; \
ROLQ $25, rBi; \
MOVQ rBe, rT1; \
ORQ rBi, rT1; \
XORQ rDe, rBa; \
ROLQ $1, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ka(oState); \
K_RT1_RCA; \
\
XORQ rDu, rBo; \
ROLQ $8, rBo; \
MOVQ rBi, rT1; \
ANDQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _ke(oState); \
K_RT1_RCE; \
\
XORQ rDa, rBu; \
ROLQ $18, rBu; \
NOTQ rBo; \
MOVQ rBo, rT1; \
ANDQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _ki(oState); \
\
MOVQ rBu, rT1; \
ORQ rBa, rT1; \
XORQ rBo, rT1; \
MOVQ rT1, _ko(oState); \
\
ANDQ rBe, rBa; \
XORQ rBu, rBa; \
MOVQ rBa, _ku(oState); \
K_RBA_RCU; \
\
/* Result m */ \
MOVQ _ga(iState), rBe; \
XORQ rDa, rBe; \
MOVQ _ke(iState), rBi; \
ROLQ $36, rBe; \
XORQ rDe, rBi; \
MOVQ _bu(iState), rBa; \
ROLQ $10, rBi; \
MOVQ rBe, rT1; \
MOVQ _mi(iState), rBo; \
ANDQ rBi, rT1; \
XORQ rDu, rBa; \
MOVQ _so(iState), rBu; \
ROLQ $27, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ma(oState); \
M_RT1_RCA; \
\
XORQ rDi, rBo; \
ROLQ $15, rBo; \
MOVQ rBi, rT1; \
ORQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _me(oState); \
M_RT1_RCE; \
\
XORQ rDo, rBu; \
ROLQ $56, rBu; \
NOTQ rBo; \
MOVQ rBo, rT1; \
ORQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _mi(oState); \
\
ORQ rBa, rBe; \
XORQ rBu, rBe; \
MOVQ rBe, _mu(oState); \
\
ANDQ rBa, rBu; \
XORQ rBo, rBu; \
MOVQ rBu, _mo(oState); \
M_RBE_RCU; \
\
/* Result s */ \
MOVQ _bi(iState), rBa; \
MOVQ _go(iState), rBe; \
MOVQ _ku(iState), rBi; \
XORQ rDi, rBa; \
MOVQ _ma(iState), rBo; \
ROLQ $62, rBa; \
XORQ rDo, rBe; \
MOVQ _se(iState), rBu; \
ROLQ $55, rBe; \
\
XORQ rDu, rBi; \
MOVQ rBa, rDu; \
XORQ rDe, rBu; \
ROLQ $2, rBu; \
ANDQ rBe, rDu; \
XORQ rBu, rDu; \
MOVQ rDu, _su(oState); \
\
ROLQ $39, rBi; \
S_RDU_RCU; \
NOTQ rBe; \
XORQ rDa, rBo; \
MOVQ rBe, rDa; \
ANDQ rBi, rDa; \
XORQ rBa, rDa; \
MOVQ rDa, _sa(oState); \
S_RDA_RCA; \
\
ROLQ $41, rBo; \
MOVQ rBi, rDe; \
ORQ rBo, rDe; \
XORQ rBe, rDe; \
MOVQ rDe, _se(oState); \
S_RDE_RCE; \
\
MOVQ rBo, rDi; \
MOVQ rBu, rDo; \
ANDQ rBu, rDi; \
ORQ rBa, rDo; \
XORQ rBi, rDi; \
XORQ rBo, rDo; \
MOVQ rDi, _si(oState); \
MOVQ rDo, _so(oState) \
// func keccakF1600(state *[25]uint64)
TEXT ·keccakF1600(SB), 0, $200-8
MOVQ state+0(FP), rpState
// Convert the user state into an internal state
NOTQ _be(rpState)
NOTQ _bi(rpState)
NOTQ _go(rpState)
NOTQ _ki(rpState)
NOTQ _mi(rpState)
NOTQ _sa(rpState)
// Execute the KeccakF permutation
MOVQ _ba(rpState), rCa
MOVQ _be(rpState), rCe
MOVQ _bu(rpState), rCu
XORQ _ga(rpState), rCa
XORQ _ge(rpState), rCe
XORQ _gu(rpState), rCu
XORQ _ka(rpState), rCa
XORQ _ke(rpState), rCe
XORQ _ku(rpState), rCu
XORQ _ma(rpState), rCa
XORQ _me(rpState), rCe
XORQ _mu(rpState), rCu
XORQ _sa(rpState), rCa
XORQ _se(rpState), rCe
MOVQ _si(rpState), rDi
MOVQ _so(rpState), rDo
XORQ _su(rpState), rCu
mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
// Revert the internal state to the user state
NOTQ _be(rpState)
NOTQ _bi(rpState)
NOTQ _go(rpState)
NOTQ _ki(rpState)
NOTQ _mi(rpState)
NOTQ _sa(rpState)
RET

19
vendor/golang.org/x/crypto/sha3/register.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.4
// +build go1.4
package sha3
import (
"crypto"
)
func init() {
crypto.RegisterHash(crypto.SHA3_224, New224)
crypto.RegisterHash(crypto.SHA3_256, New256)
crypto.RegisterHash(crypto.SHA3_384, New384)
crypto.RegisterHash(crypto.SHA3_512, New512)
}

193
vendor/golang.org/x/crypto/sha3/sha3.go generated vendored Normal file
View File

@ -0,0 +1,193 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
// spongeDirection indicates the direction bytes are flowing through the sponge.
type spongeDirection int
const (
// spongeAbsorbing indicates that the sponge is absorbing input.
spongeAbsorbing spongeDirection = iota
// spongeSqueezing indicates that the sponge is being squeezed.
spongeSqueezing
)
const (
// maxRate is the maximum size of the internal buffer. SHAKE-256
// currently needs the largest buffer.
maxRate = 168
)
type state struct {
// Generic sponge components.
a [25]uint64 // main state of the hash
buf []byte // points into storage
rate int // the number of bytes of state to use
// dsbyte contains the "domain separation" bits and the first bit of
// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
// SHA-3 and SHAKE functions by appending bitstrings to the message.
// Using a little-endian bit-ordering convention, these are "01" for SHA-3
// and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the
// padding rule from section 5.1 is applied to pad the message to a multiple
// of the rate, which involves adding a "1" bit, zero or more "0" bits, and
// a final "1" bit. We merge the first "1" bit from the padding into dsbyte,
// giving 00000110b (0x06) and 00011111b (0x1f).
// [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf
// "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and
// Extendable-Output Functions (May 2014)"
dsbyte byte
storage storageBuf
// Specific to SHA-3 and SHAKE.
outputLen int // the default output size in bytes
state spongeDirection // whether the sponge is absorbing or squeezing
}
// BlockSize returns the rate of sponge underlying this hash function.
func (d *state) BlockSize() int { return d.rate }
// Size returns the output size of the hash function in bytes.
func (d *state) Size() int { return d.outputLen }
// Reset clears the internal state by zeroing the sponge state and
// the byte buffer, and setting Sponge.state to absorbing.
func (d *state) Reset() {
// Zero the permutation's state.
for i := range d.a {
d.a[i] = 0
}
d.state = spongeAbsorbing
d.buf = d.storage.asBytes()[:0]
}
func (d *state) clone() *state {
ret := *d
if ret.state == spongeAbsorbing {
ret.buf = ret.storage.asBytes()[:len(ret.buf)]
} else {
ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate]
}
return &ret
}
// permute applies the KeccakF-1600 permutation. It handles
// any input-output buffering.
func (d *state) permute() {
switch d.state {
case spongeAbsorbing:
// If we're absorbing, we need to xor the input into the state
// before applying the permutation.
xorIn(d, d.buf)
d.buf = d.storage.asBytes()[:0]
keccakF1600(&d.a)
case spongeSqueezing:
// If we're squeezing, we need to apply the permutation before
// copying more output.
keccakF1600(&d.a)
d.buf = d.storage.asBytes()[:d.rate]
copyOut(d, d.buf)
}
}
// pads appends the domain separation bits in dsbyte, applies
// the multi-bitrate 10..1 padding rule, and permutes the state.
func (d *state) padAndPermute(dsbyte byte) {
if d.buf == nil {
d.buf = d.storage.asBytes()[:0]
}
// Pad with this instance's domain-separator bits. We know that there's
// at least one byte of space in d.buf because, if it were full,
// permute would have been called to empty it. dsbyte also contains the
// first one bit for the padding. See the comment in the state struct.
d.buf = append(d.buf, dsbyte)
zerosStart := len(d.buf)
d.buf = d.storage.asBytes()[:d.rate]
for i := zerosStart; i < d.rate; i++ {
d.buf[i] = 0
}
// This adds the final one bit for the padding. Because of the way that
// bits are numbered from the LSB upwards, the final bit is the MSB of
// the last byte.
d.buf[d.rate-1] ^= 0x80
// Apply the permutation
d.permute()
d.state = spongeSqueezing
d.buf = d.storage.asBytes()[:d.rate]
copyOut(d, d.buf)
}
// Write absorbs more data into the hash's state. It produces an error
// if more data is written to the ShakeHash after writing
func (d *state) Write(p []byte) (written int, err error) {
if d.state != spongeAbsorbing {
panic("sha3: write to sponge after read")
}
if d.buf == nil {
d.buf = d.storage.asBytes()[:0]
}
written = len(p)
for len(p) > 0 {
if len(d.buf) == 0 && len(p) >= d.rate {
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
xorIn(d, p[:d.rate])
p = p[d.rate:]
keccakF1600(&d.a)
} else {
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
todo := d.rate - len(d.buf)
if todo > len(p) {
todo = len(p)
}
d.buf = append(d.buf, p[:todo]...)
p = p[todo:]
// If the sponge is full, apply the permutation.
if len(d.buf) == d.rate {
d.permute()
}
}
}
return
}
// Read squeezes an arbitrary number of bytes from the sponge.
func (d *state) Read(out []byte) (n int, err error) {
// If we're still absorbing, pad and apply the permutation.
if d.state == spongeAbsorbing {
d.padAndPermute(d.dsbyte)
}
n = len(out)
// Now, do the squeezing.
for len(out) > 0 {
n := copy(out, d.buf)
d.buf = d.buf[n:]
out = out[n:]
// Apply the permutation if we've squeezed the sponge dry.
if len(d.buf) == 0 {
d.permute()
}
}
return
}
// Sum applies padding to the hash state and then squeezes out the desired
// number of output bytes.
func (d *state) Sum(in []byte) []byte {
// Make a copy of the original hash so that caller can keep writing
// and summing.
dup := d.clone()
hash := make([]byte, dup.outputLen)
dup.Read(hash)
return append(in, hash...)
}

287
vendor/golang.org/x/crypto/sha3/sha3_s390x.go generated vendored Normal file
View File

@ -0,0 +1,287 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
package sha3
// This file contains code for using the 'compute intermediate
// message digest' (KIMD) and 'compute last message digest' (KLMD)
// instructions to compute SHA-3 and SHAKE hashes on IBM Z.
import (
"hash"
"golang.org/x/sys/cpu"
)
// codes represent 7-bit KIMD/KLMD function codes as defined in
// the Principles of Operation.
type code uint64
const (
// function codes for KIMD/KLMD
sha3_224 code = 32
sha3_256 = 33
sha3_384 = 34
sha3_512 = 35
shake_128 = 36
shake_256 = 37
nopad = 0x100
)
// kimd is a wrapper for the 'compute intermediate message digest' instruction.
// src must be a multiple of the rate for the given function code.
//
//go:noescape
func kimd(function code, chain *[200]byte, src []byte)
// klmd is a wrapper for the 'compute last message digest' instruction.
// src padding is handled by the instruction.
//
//go:noescape
func klmd(function code, chain *[200]byte, dst, src []byte)
type asmState struct {
a [200]byte // 1600 bit state
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
rate int // equivalent to block size
storage [3072]byte // underlying storage for buf
outputLen int // output length if fixed, 0 if not
function code // KIMD/KLMD function code
state spongeDirection // whether the sponge is absorbing or squeezing
}
func newAsmState(function code) *asmState {
var s asmState
s.function = function
switch function {
case sha3_224:
s.rate = 144
s.outputLen = 28
case sha3_256:
s.rate = 136
s.outputLen = 32
case sha3_384:
s.rate = 104
s.outputLen = 48
case sha3_512:
s.rate = 72
s.outputLen = 64
case shake_128:
s.rate = 168
case shake_256:
s.rate = 136
default:
panic("sha3: unrecognized function code")
}
// limit s.buf size to a multiple of s.rate
s.resetBuf()
return &s
}
func (s *asmState) clone() *asmState {
c := *s
c.buf = c.storage[:len(s.buf):cap(s.buf)]
return &c
}
// copyIntoBuf copies b into buf. It will panic if there is not enough space to
// store all of b.
func (s *asmState) copyIntoBuf(b []byte) {
bufLen := len(s.buf)
s.buf = s.buf[:len(s.buf)+len(b)]
copy(s.buf[bufLen:], b)
}
// resetBuf points buf at storage, sets the length to 0 and sets cap to be a
// multiple of the rate.
func (s *asmState) resetBuf() {
max := (cap(s.storage) / s.rate) * s.rate
s.buf = s.storage[:0:max]
}
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
func (s *asmState) Write(b []byte) (int, error) {
if s.state != spongeAbsorbing {
panic("sha3: write to sponge after read")
}
length := len(b)
for len(b) > 0 {
if len(s.buf) == 0 && len(b) >= cap(s.buf) {
// Hash the data directly and push any remaining bytes
// into the buffer.
remainder := len(b) % s.rate
kimd(s.function, &s.a, b[:len(b)-remainder])
if remainder != 0 {
s.copyIntoBuf(b[len(b)-remainder:])
}
return length, nil
}
if len(s.buf) == cap(s.buf) {
// flush the buffer
kimd(s.function, &s.a, s.buf)
s.buf = s.buf[:0]
}
// copy as much as we can into the buffer
n := len(b)
if len(b) > cap(s.buf)-len(s.buf) {
n = cap(s.buf) - len(s.buf)
}
s.copyIntoBuf(b[:n])
b = b[n:]
}
return length, nil
}
// Read squeezes an arbitrary number of bytes from the sponge.
func (s *asmState) Read(out []byte) (n int, err error) {
n = len(out)
// need to pad if we were absorbing
if s.state == spongeAbsorbing {
s.state = spongeSqueezing
// write hash directly into out if possible
if len(out)%s.rate == 0 {
klmd(s.function, &s.a, out, s.buf) // len(out) may be 0
s.buf = s.buf[:0]
return
}
// write hash into buffer
max := cap(s.buf)
if max > len(out) {
max = (len(out)/s.rate)*s.rate + s.rate
}
klmd(s.function, &s.a, s.buf[:max], s.buf)
s.buf = s.buf[:max]
}
for len(out) > 0 {
// flush the buffer
if len(s.buf) != 0 {
c := copy(out, s.buf)
out = out[c:]
s.buf = s.buf[c:]
continue
}
// write hash directly into out if possible
if len(out)%s.rate == 0 {
klmd(s.function|nopad, &s.a, out, nil)
return
}
// write hash into buffer
s.resetBuf()
if cap(s.buf) > len(out) {
s.buf = s.buf[:(len(out)/s.rate)*s.rate+s.rate]
}
klmd(s.function|nopad, &s.a, s.buf, nil)
}
return
}
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (s *asmState) Sum(b []byte) []byte {
if s.outputLen == 0 {
panic("sha3: cannot call Sum on SHAKE functions")
}
// Copy the state to preserve the original.
a := s.a
// Hash the buffer. Note that we don't clear it because we
// aren't updating the state.
klmd(s.function, &a, nil, s.buf)
return append(b, a[:s.outputLen]...)
}
// Reset resets the Hash to its initial state.
func (s *asmState) Reset() {
for i := range s.a {
s.a[i] = 0
}
s.resetBuf()
s.state = spongeAbsorbing
}
// Size returns the number of bytes Sum will return.
func (s *asmState) Size() int {
return s.outputLen
}
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
func (s *asmState) BlockSize() int {
return s.rate
}
// Clone returns a copy of the ShakeHash in its current state.
func (s *asmState) Clone() ShakeHash {
return s.clone()
}
// new224Asm returns an assembly implementation of SHA3-224 if available,
// otherwise it returns nil.
func new224Asm() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_224)
}
return nil
}
// new256Asm returns an assembly implementation of SHA3-256 if available,
// otherwise it returns nil.
func new256Asm() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_256)
}
return nil
}
// new384Asm returns an assembly implementation of SHA3-384 if available,
// otherwise it returns nil.
func new384Asm() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_384)
}
return nil
}
// new512Asm returns an assembly implementation of SHA3-512 if available,
// otherwise it returns nil.
func new512Asm() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_512)
}
return nil
}
// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
// otherwise it returns nil.
func newShake128Asm() ShakeHash {
if cpu.S390X.HasSHA3 {
return newAsmState(shake_128)
}
return nil
}
// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
// otherwise it returns nil.
func newShake256Asm() ShakeHash {
if cpu.S390X.HasSHA3 {
return newAsmState(shake_256)
}
return nil
}

34
vendor/golang.org/x/crypto/sha3/sha3_s390x.s generated vendored Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
#include "textflag.h"
// func kimd(function code, chain *[200]byte, src []byte)
TEXT ·kimd(SB), NOFRAME|NOSPLIT, $0-40
MOVD function+0(FP), R0
MOVD chain+8(FP), R1
LMG src+16(FP), R2, R3 // R2=base, R3=len
continue:
WORD $0xB93E0002 // KIMD --, R2
BVS continue // continue if interrupted
MOVD $0, R0 // reset R0 for pre-go1.8 compilers
RET
// func klmd(function code, chain *[200]byte, dst, src []byte)
TEXT ·klmd(SB), NOFRAME|NOSPLIT, $0-64
// TODO: SHAKE support
MOVD function+0(FP), R0
MOVD chain+8(FP), R1
LMG dst+16(FP), R2, R3 // R2=base, R3=len
LMG src+40(FP), R4, R5 // R4=base, R5=len
continue:
WORD $0xB93F0024 // KLMD R2, R4
BVS continue // continue if interrupted
MOVD $0, R0 // reset R0 for pre-go1.8 compilers
RET

173
vendor/golang.org/x/crypto/sha3/shake.go generated vendored Normal file
View File

@ -0,0 +1,173 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
// This file defines the ShakeHash interface, and provides
// functions for creating SHAKE and cSHAKE instances, as well as utility
// functions for hashing bytes to arbitrary-length output.
//
//
// SHAKE implementation is based on FIPS PUB 202 [1]
// cSHAKE implementations is based on NIST SP 800-185 [2]
//
// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
// [2] https://doi.org/10.6028/NIST.SP.800-185
import (
"encoding/binary"
"io"
)
// ShakeHash defines the interface to hash functions that
// support arbitrary-length output.
type ShakeHash interface {
// Write absorbs more data into the hash's state. It panics if input is
// written to it after output has been read from it.
io.Writer
// Read reads more output from the hash; reading affects the hash's
// state. (ShakeHash.Read is thus very different from Hash.Sum)
// It never returns an error.
io.Reader
// Clone returns a copy of the ShakeHash in its current state.
Clone() ShakeHash
// Reset resets the ShakeHash to its initial state.
Reset()
}
// cSHAKE specific context
type cshakeState struct {
*state // SHA-3 state context and Read/Write operations
// initBlock is the cSHAKE specific initialization set of bytes. It is initialized
// by newCShake function and stores concatenation of N followed by S, encoded
// by the method specified in 3.3 of [1].
// It is stored here in order for Reset() to be able to put context into
// initial state.
initBlock []byte
}
// Consts for configuring initial SHA-3 state
const (
dsbyteShake = 0x1f
dsbyteCShake = 0x04
rate128 = 168
rate256 = 136
)
func bytepad(input []byte, w int) []byte {
// leftEncode always returns max 9 bytes
buf := make([]byte, 0, 9+len(input)+w)
buf = append(buf, leftEncode(uint64(w))...)
buf = append(buf, input...)
padlen := w - (len(buf) % w)
return append(buf, make([]byte, padlen)...)
}
func leftEncode(value uint64) []byte {
var b [9]byte
binary.BigEndian.PutUint64(b[1:], value)
// Trim all but last leading zero bytes
i := byte(1)
for i < 8 && b[i] == 0 {
i++
}
// Prepend number of encoded bytes
b[i-1] = 9 - i
return b[i-1:]
}
func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
// leftEncode returns max 9 bytes
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...)
c.initBlock = append(c.initBlock, N...)
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...)
c.initBlock = append(c.initBlock, S...)
c.Write(bytepad(c.initBlock, c.rate))
return &c
}
// Reset resets the hash to initial state.
func (c *cshakeState) Reset() {
c.state.Reset()
c.Write(bytepad(c.initBlock, c.rate))
}
// Clone returns copy of a cSHAKE context within its current state.
func (c *cshakeState) Clone() ShakeHash {
b := make([]byte, len(c.initBlock))
copy(b, c.initBlock)
return &cshakeState{state: c.clone(), initBlock: b}
}
// Clone returns copy of SHAKE context within its current state.
func (c *state) Clone() ShakeHash {
return c.clone()
}
// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
// Its generic security strength is 128 bits against all attacks if at
// least 32 bytes of its output are used.
func NewShake128() ShakeHash {
if h := newShake128Asm(); h != nil {
return h
}
return &state{rate: rate128, dsbyte: dsbyteShake}
}
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
// Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used.
func NewShake256() ShakeHash {
if h := newShake256Asm(); h != nil {
return h
}
return &state{rate: rate256, dsbyte: dsbyteShake}
}
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
// a customizable variant of SHAKE128.
// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
// desired. S is a customization byte string used for domain separation - two cSHAKE
// computations on same input with different S yield unrelated outputs.
// When N and S are both empty, this is equivalent to NewShake128.
func NewCShake128(N, S []byte) ShakeHash {
if len(N) == 0 && len(S) == 0 {
return NewShake128()
}
return newCShake(N, S, rate128, dsbyteCShake)
}
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
// a customizable variant of SHAKE256.
// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
// desired. S is a customization byte string used for domain separation - two cSHAKE
// computations on same input with different S yield unrelated outputs.
// When N and S are both empty, this is equivalent to NewShake256.
func NewCShake256(N, S []byte) ShakeHash {
if len(N) == 0 && len(S) == 0 {
return NewShake256()
}
return newCShake(N, S, rate256, dsbyteCShake)
}
// ShakeSum128 writes an arbitrary-length digest of data into hash.
func ShakeSum128(hash, data []byte) {
h := NewShake128()
h.Write(data)
h.Read(hash)
}
// ShakeSum256 writes an arbitrary-length digest of data into hash.
func ShakeSum256(hash, data []byte) {
h := NewShake256()
h.Write(data)
h.Read(hash)
}

20
vendor/golang.org/x/crypto/sha3/shake_generic.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
// +build !gc purego !s390x
package sha3
// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
// otherwise it returns nil.
func newShake128Asm() ShakeHash {
return nil
}
// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
// otherwise it returns nil.
func newShake256Asm() ShakeHash {
return nil
}

24
vendor/golang.org/x/crypto/sha3/xor.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (!amd64 && !386 && !ppc64le) || purego
// +build !amd64,!386,!ppc64le purego
package sha3
// A storageBuf is an aligned array of maxRate bytes.
type storageBuf [maxRate]byte
func (b *storageBuf) asBytes() *[maxRate]byte {
return (*[maxRate]byte)(b)
}
var (
xorIn = xorInGeneric
copyOut = copyOutGeneric
xorInUnaligned = xorInGeneric
copyOutUnaligned = copyOutGeneric
)
const xorImplementationUnaligned = "generic"

28
vendor/golang.org/x/crypto/sha3/xor_generic.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
import "encoding/binary"
// xorInGeneric xors the bytes in buf into the state; it
// makes no non-portable assumptions about memory layout
// or alignment.
func xorInGeneric(d *state, buf []byte) {
n := len(buf) / 8
for i := 0; i < n; i++ {
a := binary.LittleEndian.Uint64(buf)
d.a[i] ^= a
buf = buf[8:]
}
}
// copyOutGeneric copies uint64s to a byte buffer.
func copyOutGeneric(d *state, b []byte) {
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, d.a[i])
b = b[8:]
}
}

Some files were not shown because too many files have changed in this diff Show More