Explorar o código

Merge pull request #638 from adotkhan/bytes

Byte generation with goregen
Rod Hynes %!s(int64=2) %!d(string=hai) anos
pai
achega
f925cd42ce

+ 2 - 0
.github/workflows/tests.yml

@@ -80,6 +80,7 @@ jobs:
           go test -v -race ./psiphon/common/accesscontrol
           go test -v -race ./psiphon/common/accesscontrol
           go test -v -race ./psiphon/common/crypto/ssh
           go test -v -race ./psiphon/common/crypto/ssh
           go test -v -race ./psiphon/common/fragmentor
           go test -v -race ./psiphon/common/fragmentor
+          go test -v -race ./psiphon/common/regen
           go test -v -race ./psiphon/common/monotime
           go test -v -race ./psiphon/common/monotime
           go test -v -race ./psiphon/common/obfuscator
           go test -v -race ./psiphon/common/obfuscator
           go test -v -race ./psiphon/common/osl
           go test -v -race ./psiphon/common/osl
@@ -111,6 +112,7 @@ jobs:
           go test -v -covermode=count -coverprofile=accesscontrol.coverprofile ./psiphon/common/accesscontrol
           go test -v -covermode=count -coverprofile=accesscontrol.coverprofile ./psiphon/common/accesscontrol
           go test -v -covermode=count -coverprofile=ssh.coverprofile ./psiphon/common/crypto/ssh
           go test -v -covermode=count -coverprofile=ssh.coverprofile ./psiphon/common/crypto/ssh
           go test -v -covermode=count -coverprofile=fragmentor.coverprofile ./psiphon/common/fragmentor
           go test -v -covermode=count -coverprofile=fragmentor.coverprofile ./psiphon/common/fragmentor
+          go test -v -covermode=count -coverprofile=regen.coverprofile ./psiphon/common/regen
           go test -v -covermode=count -coverprofile=monotime.coverprofile ./psiphon/common/monotime
           go test -v -covermode=count -coverprofile=monotime.coverprofile ./psiphon/common/monotime
           go test -v -covermode=count -coverprofile=obfuscator.coverprofile ./psiphon/common/obfuscator
           go test -v -covermode=count -coverprofile=obfuscator.coverprofile ./psiphon/common/obfuscator
           go test -v -covermode=count -coverprofile=osl.coverprofile ./psiphon/common/osl
           go test -v -covermode=count -coverprofile=osl.coverprofile ./psiphon/common/osl

+ 0 - 3
go.mod

@@ -47,7 +47,6 @@ require (
 	github.com/stretchr/testify v1.7.1
 	github.com/stretchr/testify v1.7.1
 	github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8
 	github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8
 	github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78
 	github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78
-	github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
 	golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
 	golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
 	golang.org/x/net v0.7.0
 	golang.org/x/net v0.7.0
 	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
 	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
@@ -70,7 +69,6 @@ require (
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 // indirect
 	github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
-	github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4 // indirect
 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
 	github.com/josharian/native v1.0.0 // indirect
 	github.com/josharian/native v1.0.0 // indirect
 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
@@ -83,7 +81,6 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/quic-go/qpack v0.4.0 // indirect
 	github.com/quic-go/qpack v0.4.0 // indirect
 	github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 // indirect
 	github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 // indirect
-	github.com/smartystreets/goconvey v1.7.2 // indirect
 	gitlab.com/yawning/obfs4.git v0.0.0-20190120164510-816cff15f425 // indirect
 	gitlab.com/yawning/obfs4.git v0.0.0-20190120164510-816cff15f425 // indirect
 	golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect
 	golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect

+ 0 - 84
go.sum

@@ -10,20 +10,10 @@ github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7 h1:Hx/NCZTnvoKZu
 github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7/go.mod h1:alTtZBo3j4AWFvUrAH6F5ZaHcTj4G5Y01nHz8dkU6vU=
 github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7/go.mod h1:alTtZBo3j4AWFvUrAH6F5ZaHcTj4G5Y01nHz8dkU6vU=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFXwLV0noxYhbJbLmkV4iaVW3xNnj6xcCNHo=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFXwLV0noxYhbJbLmkV4iaVW3xNnj6xcCNHo=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
-github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20220329170849-8c3d6ec797db h1:pf7KIxOA1C1omtCMfbodIBkAiNbdlp90h3pyHdhryKg=
-github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20220329170849-8c3d6ec797db/go.mod h1:LwOQkgHs4ZDxcz+JUXmun7keQsjidtnY5n6H10rL9rE=
 github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091 h1:Kv0LQQ3joUp8s2z36aigpNgNyiLiExT/OS9KOC/L/gI=
 github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091 h1:Kv0LQQ3joUp8s2z36aigpNgNyiLiExT/OS9KOC/L/gI=
 github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091/go.mod h1:0IvfcPDkLvBkir+WGq3E0shsx+TLasdcl8ojVWWTflE=
 github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091/go.mod h1:0IvfcPDkLvBkir+WGq3E0shsx+TLasdcl8ojVWWTflE=
-github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221011165004-2c4f1e9e222f h1:v7jqQrGDBp1SCX/GoWGMWYYNnbR4tor1Mj54P/G0GPk=
-github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221011165004-2c4f1e9e222f/go.mod h1:JH6mtnGhMhiqzymiFohsYx90SCEhSZCw7u7DJGBnu1k=
 github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082 h1:arVlc3JYvckFXGyB8N30ul8AmA+rDuLolPRYMDHzgTU=
 github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082 h1:arVlc3JYvckFXGyB8N30ul8AmA+rDuLolPRYMDHzgTU=
 github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082/go.mod h1:mHM/QFYc02W9MKJ/Ux5XGOKP4OImosPeQUO7XAaXs0E=
 github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082/go.mod h1:mHM/QFYc02W9MKJ/Ux5XGOKP4OImosPeQUO7XAaXs0E=
-github.com/Psiphon-Labs/quic-go v0.0.0-20221012142746-f3b75b440582 h1:T8I7L8tHDID/5dEl9j/ftpDIfcLpWyCv8Mq7i5i/Ak8=
-github.com/Psiphon-Labs/quic-go v0.0.0-20221012142746-f3b75b440582/go.mod h1:Dcp6EsbioehAvUSMTMbqFV6Z+q+IApml2Q3r8eXQS5M=
-github.com/Psiphon-Labs/quic-go v0.0.0-20221014165902-1b7c3975fcf3 h1:BKSZdSkhOGV23vBl4cYPabzkRlWk5Zm7Snpo4i0lSXQ=
-github.com/Psiphon-Labs/quic-go v0.0.0-20221014165902-1b7c3975fcf3/go.mod h1:llhtSl7dUXTssUN4m4MjUDJrULGNxgZBMKYjExuk6EM=
-github.com/Psiphon-Labs/quic-go v0.0.0-20230124165616-fe8e9a215a66 h1:xTGnmXqEEUphnohYLGwGrlVzWPNpTNAFViJYxQUMHRU=
-github.com/Psiphon-Labs/quic-go v0.0.0-20230124165616-fe8e9a215a66/go.mod h1:cu4yhfHkyt+uQ9FFFjTpjCjcQYf52ntEAyoV4Zg0+fg=
 github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc h1:FUmGSvMiMbf1tFXWbK0+N7+5zBhOol8CHQdpB4ZQlDg=
 github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc h1:FUmGSvMiMbf1tFXWbK0+N7+5zBhOol8CHQdpB4ZQlDg=
 github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc/go.mod h1:cu4yhfHkyt+uQ9FFFjTpjCjcQYf52ntEAyoV4Zg0+fg=
 github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc/go.mod h1:cu4yhfHkyt+uQ9FFFjTpjCjcQYf52ntEAyoV4Zg0+fg=
 github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad h1:m6HS84+b5xDPLj7D/ya1CeixyaHOCZoMbBilJ48y+Ts=
 github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad h1:m6HS84+b5xDPLj7D/ya1CeixyaHOCZoMbBilJ48y+Ts=
@@ -65,9 +55,6 @@ github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:gNh8
 github.com/florianl/go-nfqueue v1.1.1-0.20200829120558-a2f196e98ab0 h1:7ZJyJV4KiWBijCCzUPvVaqxsDxO36+KD0XKBdEN3I+8=
 github.com/florianl/go-nfqueue v1.1.1-0.20200829120558-a2f196e98ab0 h1:7ZJyJV4KiWBijCCzUPvVaqxsDxO36+KD0XKBdEN3I+8=
 github.com/florianl/go-nfqueue v1.1.1-0.20200829120558-a2f196e98ab0/go.mod h1:2z3Tfqwv2ueuK6h563xUHRcCh1mv38wS9EjiWiesk84=
 github.com/florianl/go-nfqueue v1.1.1-0.20200829120558-a2f196e98ab0/go.mod h1:2z3Tfqwv2ueuK6h563xUHRcCh1mv38wS9EjiWiesk84=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/gobwas/glob v0.2.4-0.20180402141543-f00a7392b439 h1:T6zlOdzrYuHf6HUKujm9bzkzbZ5Iv/xf6rs8BHZDpoI=
 github.com/gobwas/glob v0.2.4-0.20180402141543-f00a7392b439 h1:T6zlOdzrYuHf6HUKujm9bzkzbZ5Iv/xf6rs8BHZDpoI=
@@ -76,19 +63,11 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18h
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 h1:yztvEbaW/qZGubeP7+Lug7PXl7NBfilUK6mw3jq25gQ=
 github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 h1:yztvEbaW/qZGubeP7+Lug7PXl7NBfilUK6mw3jq25gQ=
 github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -99,18 +78,13 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gopacket v1.1.19-0.20200831200443-df1bbd09a561 h1:VB5cLlMqQWruyqG6OW/EHDLUawT/hel1I3ElBE4iHg0=
 github.com/google/gopacket v1.1.19-0.20200831200443-df1bbd09a561 h1:VB5cLlMqQWruyqG6OW/EHDLUawT/hel1I3ElBE4iHg0=
 github.com/google/gopacket v1.1.19-0.20200831200443-df1bbd09a561/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
 github.com/google/gopacket v1.1.19-0.20200831200443-df1bbd09a561/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
-github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4 h1:OL2d27ueTKnlQJoqLW2fc9pWYulFnJYLWzomGV7HqZo=
-github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/grafov/m3u8 v0.0.0-20171211212457-6ab8f28ed427 h1:xh96CCAZTX8LJPFoOVRgTwZbn2DvJl8fyCyivohhSIg=
 github.com/grafov/m3u8 v0.0.0-20171211212457-6ab8f28ed427 h1:xh96CCAZTX8LJPFoOVRgTwZbn2DvJl8fyCyivohhSIg=
 github.com/grafov/m3u8 v0.0.0-20171211212457-6ab8f28ed427/go.mod h1:PdjzaU/pJUo4jTIn2rcgMFs+HqBGl/sPJLr8BI0Xq/I=
 github.com/grafov/m3u8 v0.0.0-20171211212457-6ab8f28ed427/go.mod h1:PdjzaU/pJUo4jTIn2rcgMFs+HqBGl/sPJLr8BI0Xq/I=
 github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
 github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
 github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
 github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
 github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -126,8 +100,6 @@ github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9
 github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
 github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
 github.com/jsimonetti/rtnetlink v0.0.0-20210721205614-4cc3c1489576 h1:dH/k0qzR1oouF25AoMwH6FXOr16zV4WZFcYnZGpqro0=
 github.com/jsimonetti/rtnetlink v0.0.0-20210721205614-4cc3c1489576 h1:dH/k0qzR1oouF25AoMwH6FXOr16zV4WZFcYnZGpqro0=
 github.com/jsimonetti/rtnetlink v0.0.0-20210721205614-4cc3c1489576/go.mod h1:qdKhcKUxYn3/QvneOvPWXXMPqktEBHnCW98wUTA3rmA=
 github.com/jsimonetti/rtnetlink v0.0.0-20210721205614-4cc3c1489576/go.mod h1:qdKhcKUxYn3/QvneOvPWXXMPqktEBHnCW98wUTA3rmA=
-github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
 github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
 github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
 github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
@@ -171,17 +143,8 @@ github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJ
 github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
 github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
 github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
 github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
-github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
 github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
 github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
 github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
 github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70 h1:2skBNJsk5emoC/p8tOS9DQL5Ia7ND1UNEdmgfvP+mPI=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70 h1:2skBNJsk5emoC/p8tOS9DQL5Ia7ND1UNEdmgfvP+mPI=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
@@ -206,10 +169,6 @@ github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 h1:ML7ZNtcln
 github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507/go.mod h1:DbI1gxrXI2jRGw7XGEUZQOOMd6PsnKzRrCKabvvMrwM=
 github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507/go.mod h1:DbI1gxrXI2jRGw7XGEUZQOOMd6PsnKzRrCKabvvMrwM=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
-github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
-github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
-github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -222,18 +181,13 @@ github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78 h1:9sreu9e9K
 github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78/go.mod h1:HazXTRLhXFyq80TQp7PUXi6BKE6mS+ydEdzEqNBKopQ=
 github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78/go.mod h1:HazXTRLhXFyq80TQp7PUXi6BKE6mS+ydEdzEqNBKopQ=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea h1:CyhwejzVGvZ3Q2PSbQ4NRRYn+ZWv5eS1vlaEusT+bAI=
-github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea/go.mod h1:eNr558nEUjP8acGw8FFjTeWvSgU1stO7FAO6eknhHe4=
 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
 golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
 golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20221011184403-17211926a99e h1:iNN2j45/SlljvEHC/iL9vmadnQA6d1/6bXaZw6Quv20=
-golang.org/x/exp v0.0.0-20221011184403-17211926a99e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
 golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
 golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -242,15 +196,12 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -264,33 +215,22 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
 golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 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/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -311,29 +251,19 @@ golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
-golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
 golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
 golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
 golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
@@ -343,28 +273,14 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
 google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 2 - 2
psiphon/common/parameters/frontingSpec.go

@@ -24,7 +24,7 @@ import (
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
-	regen "github.com/zach-klippenstein/goregen"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/regen"
 )
 )
 
 
 // FrontingSpecs is a list of domain fronting specs.
 // FrontingSpecs is a list of domain fronting specs.
@@ -73,7 +73,7 @@ func (specs FrontingSpecs) SelectParameters() (
 		return "", "", "", "", nil, "", errors.TraceNew("missing fronting address")
 		return "", "", "", "", nil, "", errors.TraceNew("missing fronting address")
 	}
 	}
 
 
-	frontingDialAddr, err := regen.Generate(
+	frontingDialAddr, err := regen.GenerateString(
 		spec.Addresses[prng.Intn(len(spec.Addresses))])
 		spec.Addresses[prng.Intn(len(spec.Addresses))])
 	if err != nil {
 	if err != nil {
 		return "", "", "", "", nil, "", errors.Trace(err)
 		return "", "", "", "", nil, "", errors.Trace(err)

+ 0 - 0
vendor/github.com/zach-klippenstein/goregen/LICENSE.txt → psiphon/common/regen/LICENSE.txt


+ 55 - 7
vendor/github.com/zach-klippenstein/goregen/char_class.go → psiphon/common/regen/char_class.go

@@ -14,10 +14,30 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 */
 */
 
 
+/*
+ * Copyright (c) 2023, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
 package regen
 package regen
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"math"
 )
 )
 
 
 // CharClass represents a regular expression character class as a list of ranges.
 // CharClass represents a regular expression character class as a list of ranges.
@@ -57,7 +77,7 @@ e.g.
 func parseCharClass(runes []rune) *tCharClass {
 func parseCharClass(runes []rune) *tCharClass {
 	var totalSize int32
 	var totalSize int32
 	numRanges := len(runes) / 2
 	numRanges := len(runes) / 2
-	ranges := make([]tCharClassRange, numRanges, numRanges)
+	ranges := make([]tCharClassRange, numRanges)
 
 
 	for i := 0; i < numRanges; i++ {
 	for i := 0; i < numRanges; i++ {
 		start := runes[i*2]
 		start := runes[i*2]
@@ -79,6 +99,38 @@ func parseCharClass(runes []rune) *tCharClass {
 	return &tCharClass{ranges, totalSize}
 	return &tCharClass{ranges, totalSize}
 }
 }
 
 
+// parseByteClass parses character classes only for byte values (0-255).
+// Returns nil if runes does not contain any byte values.
+//
+// Note:
+// If an end range is greater than 255, it is truncated to 255.
+func parseByteClass(runes []rune) *tCharClass {
+	var totalSize int32
+
+	var ranges []tCharClassRange
+	for i := 0; i < len(runes)-1; i += 2 {
+		start := runes[i]
+		end := runes[i+1]
+
+		var r tCharClassRange
+
+		if start <= math.MaxUint8 {
+			if end > math.MaxUint8 {
+				end = math.MaxUint8
+			}
+			r = newCharClassRange(start, end)
+			ranges = append(ranges, r)
+			totalSize += r.Size
+		}
+	}
+
+	if len(ranges) == 0 {
+		return nil
+	}
+
+	return &tCharClass{ranges, totalSize}
+}
+
 // GetRuneAt gets a rune from CharClass as a contiguous array of runes.
 // GetRuneAt gets a rune from CharClass as a contiguous array of runes.
 func (class *tCharClass) GetRuneAt(i int32) rune {
 func (class *tCharClass) GetRuneAt(i int32) rune {
 	for _, r := range class.Ranges {
 	for _, r := range class.Ranges {
@@ -95,10 +147,6 @@ func (class *tCharClass) String() string {
 }
 }
 
 
 func newCharClassRange(start rune, end rune) tCharClassRange {
 func newCharClassRange(start rune, end rune) tCharClassRange {
-	if start < 1 {
-		panic("char class range cannot contain runes less than 1")
-	}
-
 	size := end - start + 1
 	size := end - start + 1
 
 
 	if size < 1 {
 	if size < 1 {
@@ -113,8 +161,8 @@ func newCharClassRange(start rune, end rune) tCharClassRange {
 
 
 func (r tCharClassRange) String() string {
 func (r tCharClassRange) String() string {
 	if r.Size == 1 {
 	if r.Size == 1 {
-		return fmt.Sprintf("%s:1", runesToString(r.Start))
+		return fmt.Sprintf("%s:1", runesToUTF8(r.Start))
 	}
 	}
-	return fmt.Sprintf("%s-%s:%d", runesToString(r.Start), runesToString(r.Start+rune(r.Size-1)), r.Size)
+	return fmt.Sprintf("%s-%s:%d", runesToUTF8(r.Start), runesToUTF8(r.Start+rune(r.Size-1)), r.Size)
 
 
 }
 }

+ 0 - 0
vendor/github.com/zach-klippenstein/goregen/generator_error.go → psiphon/common/regen/generator_error.go


+ 45 - 0
psiphon/common/regen/generator_error_test.go

@@ -0,0 +1,45 @@
+/*
+Copyright 2014 Zachary Klippenstein
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package regen
+
+import (
+	"errors"
+	"testing"
+)
+
+func TestGeneratorError(t *testing.T) {
+
+	t.Run("Handles nil cause", func(t *testing.T) {
+		err := generatorError(nil, "msg")
+		if err == nil {
+			t.Fatal("Expected error, got nil")
+		}
+		if err.Error() != "msg" {
+			t.Fatalf("Expected error message 'msg', got '%s'", err.Error())
+		}
+	})
+
+	t.Run("Formats", func(t *testing.T) {
+		err := generatorError(errors.New("cause"), "msg %s", "arg")
+		if err == nil {
+			t.Fatal("Expected error, got nil")
+		}
+		if err.Error() != "msg arg\ncaused by cause" {
+			t.Fatalf("Expected error message 'msg arg\ncaused by cause', got '%s'", err.Error())
+		}
+	})
+}

+ 76 - 24
vendor/github.com/zach-klippenstein/goregen/internal_generator.go → psiphon/common/regen/internal_generator.go

@@ -14,6 +14,25 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 */
 */
 
 
+/*
+ * Copyright (c) 2023, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
 package regen
 package regen
 
 
 import (
 import (
@@ -56,10 +75,10 @@ func init() {
 
 
 type internalGenerator struct {
 type internalGenerator struct {
 	Name         string
 	Name         string
-	GenerateFunc func() string
+	GenerateFunc func() ([]byte, error)
 }
 }
 
 
-func (gen *internalGenerator) Generate() string {
+func (gen *internalGenerator) Generate() ([]byte, error) {
 	return gen.GenerateFunc()
 	return gen.GenerateFunc()
 }
 }
 
 
@@ -69,7 +88,7 @@ func (gen *internalGenerator) String() string {
 
 
 // Create a new generator for each expression in regexps.
 // Create a new generator for each expression in regexps.
 func newGenerators(regexps []*syntax.Regexp, args *GeneratorArgs) ([]*internalGenerator, error) {
 func newGenerators(regexps []*syntax.Regexp, args *GeneratorArgs) ([]*internalGenerator, error) {
-	generators := make([]*internalGenerator, len(regexps), len(regexps))
+	generators := make([]*internalGenerator, len(regexps))
 	var err error
 	var err error
 
 
 	// create a generator for each alternate pattern
 	// create a generator for each alternate pattern
@@ -98,35 +117,48 @@ func newGenerator(regexp *syntax.Regexp, args *GeneratorArgs) (generator *intern
 
 
 // Generator that does nothing.
 // Generator that does nothing.
 func noop(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func noop(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
-	return &internalGenerator{regexp.String(), func() string {
-		return ""
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
+		return []byte{}, nil
 	}}, nil
 	}}, nil
 }
 }
 
 
 func opEmptyMatch(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func opEmptyMatch(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 	enforceOp(regexp, syntax.OpEmptyMatch)
 	enforceOp(regexp, syntax.OpEmptyMatch)
-	return &internalGenerator{regexp.String(), func() string {
-		return ""
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
+		return []byte{}, nil
 	}}, nil
 	}}, nil
 }
 }
 
 
 func opLiteral(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func opLiteral(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 	enforceOp(regexp, syntax.OpLiteral)
 	enforceOp(regexp, syntax.OpLiteral)
-	return &internalGenerator{regexp.String(), func() string {
-		return runesToString(regexp.Rune...)
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
+		if args.ByteMode {
+			return runesToBytes(regexp.Rune...)
+		} else {
+			return runesToUTF8(regexp.Rune...), nil
+		}
 	}}, nil
 	}}, nil
 }
 }
 
 
 func opAnyChar(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func opAnyChar(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 	enforceOp(regexp, syntax.OpAnyChar)
 	enforceOp(regexp, syntax.OpAnyChar)
-	return &internalGenerator{regexp.String(), func() string {
-		return runesToString(rune(args.rng.Int31()))
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
+		if args.ByteMode {
+			return runesToBytes(rune(args.rng.Intn(math.MaxUint8 + 1)))
+		} else {
+			return runesToUTF8(rune(args.rng.Int31())), nil
+		}
 	}}, nil
 	}}, nil
 }
 }
 
 
 func opAnyCharNotNl(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func opAnyCharNotNl(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 	enforceOp(regexp, syntax.OpAnyCharNotNL)
 	enforceOp(regexp, syntax.OpAnyCharNotNL)
-	charClass := newCharClass(1, rune(math.MaxInt32))
+	var charClass *tCharClass
+	if args.ByteMode {
+		charClass = newCharClass(0, rune(math.MaxUint8))
+	} else {
+		charClass = newCharClass(1, rune(math.MaxInt32))
+	}
 	return createCharClassGenerator(regexp.String(), charClass, args)
 	return createCharClassGenerator(regexp.String(), charClass, args)
 }
 }
 
 
@@ -154,7 +186,15 @@ func opRepeat(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, e
 // classes that respect it.
 // classes that respect it.
 func opCharClass(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 func opCharClass(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator, error) {
 	enforceOp(regexp, syntax.OpCharClass)
 	enforceOp(regexp, syntax.OpCharClass)
-	charClass := parseCharClass(regexp.Rune)
+	var charClass *tCharClass
+	if args.ByteMode {
+		charClass = parseByteClass(regexp.Rune)
+		if charClass == nil {
+			return nil, fmt.Errorf("invalid byte class: /%s/", regexp)
+		}
+	} else {
+		charClass = parseCharClass(regexp.Rune)
+	}
 	return createCharClassGenerator(regexp.String(), charClass, args)
 	return createCharClassGenerator(regexp.String(), charClass, args)
 }
 }
 
 
@@ -166,12 +206,16 @@ func opConcat(regexp *syntax.Regexp, genArgs *GeneratorArgs) (*internalGenerator
 		return nil, generatorError(err, "error creating generators for concat pattern /%s/", regexp)
 		return nil, generatorError(err, "error creating generators for concat pattern /%s/", regexp)
 	}
 	}
 
 
-	return &internalGenerator{regexp.String(), func() string {
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
 		var result bytes.Buffer
 		var result bytes.Buffer
 		for _, generator := range generators {
 		for _, generator := range generators {
-			result.WriteString(generator.Generate())
+			gen, err := generator.Generate()
+			if err != nil {
+				return nil, err
+			}
+			result.Write(gen)
 		}
 		}
-		return result.String()
+		return result.Bytes(), nil
 	}}, nil
 	}}, nil
 }
 }
 
 
@@ -185,7 +229,7 @@ func opAlternate(regexp *syntax.Regexp, genArgs *GeneratorArgs) (*internalGenera
 
 
 	numGens := len(generators)
 	numGens := len(generators)
 
 
-	return &internalGenerator{regexp.String(), func() string {
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
 		i := genArgs.rng.Intn(numGens)
 		i := genArgs.rng.Intn(numGens)
 		generator := generators[i]
 		generator := generators[i]
 		return generator.Generate()
 		return generator.Generate()
@@ -208,12 +252,12 @@ func opCapture(regexp *syntax.Regexp, args *GeneratorArgs) (*internalGenerator,
 	// Group indices are 0-based, but index 0 is the whole expression.
 	// Group indices are 0-based, but index 0 is the whole expression.
 	index := regexp.Cap - 1
 	index := regexp.Cap - 1
 
 
-	return &internalGenerator{regexp.String(), func() string {
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
 		return args.CaptureGroupHandler(index, regexp.Name, groupRegexp, generator, args)
 		return args.CaptureGroupHandler(index, regexp.Name, groupRegexp, generator, args)
 	}}, nil
 	}}, nil
 }
 }
 
 
-func defaultCaptureGroupHandler(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) string {
+func defaultCaptureGroupHandler(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) ([]byte, error) {
 	return generator.Generate()
 	return generator.Generate()
 }
 }
 
 
@@ -234,10 +278,14 @@ func enforceSingleSub(regexp *syntax.Regexp) error {
 }
 }
 
 
 func createCharClassGenerator(name string, charClass *tCharClass, args *GeneratorArgs) (*internalGenerator, error) {
 func createCharClassGenerator(name string, charClass *tCharClass, args *GeneratorArgs) (*internalGenerator, error) {
-	return &internalGenerator{name, func() string {
+	return &internalGenerator{name, func() ([]byte, error) {
 		i := args.rng.Int31n(charClass.TotalSize)
 		i := args.rng.Int31n(charClass.TotalSize)
 		r := charClass.GetRuneAt(i)
 		r := charClass.GetRuneAt(i)
-		return runesToString(r)
+		if args.ByteMode {
+			return runesToBytes(r)
+		} else {
+			return runesToUTF8(r), nil
+		}
 	}}, nil
 	}}, nil
 }
 }
 
 
@@ -259,13 +307,17 @@ func createRepeatingGenerator(regexp *syntax.Regexp, genArgs *GeneratorArgs, min
 		max = int(genArgs.MaxUnboundedRepeatCount)
 		max = int(genArgs.MaxUnboundedRepeatCount)
 	}
 	}
 
 
-	return &internalGenerator{regexp.String(), func() string {
+	return &internalGenerator{regexp.String(), func() ([]byte, error) {
 		n := min + genArgs.rng.Intn(max-min+1)
 		n := min + genArgs.rng.Intn(max-min+1)
 
 
 		var result bytes.Buffer
 		var result bytes.Buffer
 		for i := 0; i < n; i++ {
 		for i := 0; i < n; i++ {
-			result.WriteString(generator.Generate())
+			value, err := generator.Generate()
+			if err != nil {
+				return nil, err
+			}
+			result.Write(value)
 		}
 		}
-		return result.String()
+		return result.Bytes(), nil
 	}}, nil
 	}}, nil
 }
 }

+ 70 - 13
vendor/github.com/zach-klippenstein/goregen/regen.go → psiphon/common/regen/regen.go

@@ -14,19 +14,40 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 */
 */
 
 
+/*
+ * Copyright (c) 2023, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
 /*
 /*
 Package regen is a library for generating random strings from regular expressions.
 Package regen is a library for generating random strings from regular expressions.
 The generated strings will match the expressions they were generated from. Similar
 The generated strings will match the expressions they were generated from. Similar
 to Ruby's randexp library.
 to Ruby's randexp library.
 
 
 E.g.
 E.g.
-	regen.Generate("[a-z0-9]{1,64}")
+
+	regen.GenerateString("[a-z0-9]{1,64}")
+
 will return a lowercase alphanumeric string
 will return a lowercase alphanumeric string
 between 1 and 64 characters long.
 between 1 and 64 characters long.
 
 
 Expressions are parsed using the Go standard library's parser: http://golang.org/pkg/regexp/syntax/.
 Expressions are parsed using the Go standard library's parser: http://golang.org/pkg/regexp/syntax/.
 
 
-Constraints
+# Constraints
 
 
 "." will generate any character, not necessarily a printable one.
 "." will generate any character, not necessarily a printable one.
 
 
@@ -34,7 +55,7 @@ Constraints
 If you care about the maximum number, specify it explicitly in the expression,
 If you care about the maximum number, specify it explicitly in the expression,
 e.g. "x{0,256}".
 e.g. "x{0,256}".
 
 
-Flags
+# Flags
 
 
 Flags can be passed to the parser by setting them in the GeneratorArgs struct.
 Flags can be passed to the parser by setting them in the GeneratorArgs struct.
 Newline flags are respected, and newlines won't be generated unless the appropriate flags for
 Newline flags are respected, and newlines won't be generated unless the appropriate flags for
@@ -48,7 +69,7 @@ The Perl character class flag is supported, and required if the pattern contains
 
 
 Unicode groups are not supported at this time. Support may be added in the future.
 Unicode groups are not supported at this time. Support may be added in the future.
 
 
-Concurrent Use
+# Concurrent Use
 
 
 A generator can safely be used from multiple goroutines without locking.
 A generator can safely be used from multiple goroutines without locking.
 
 
@@ -63,7 +84,7 @@ the same source may get the same output. While obviously not cryptographically s
 benefit outweighs the risk of collisions. If you really care about preventing this, the solution is simple: don't
 benefit outweighs the risk of collisions. If you really care about preventing this, the solution is simple: don't
 call a single Generator from multiple goroutines.
 call a single Generator from multiple goroutines.
 
 
-Benchmarks
+# Benchmarks
 
 
 Benchmarks are included for creating and running generators for limited-length,
 Benchmarks are included for creating and running generators for limited-length,
 complex regexes, and simple, highly-repetitive regexes.
 complex regexes, and simple, highly-repetitive regexes.
@@ -71,6 +92,7 @@ complex regexes, and simple, highly-repetitive regexes.
 	go test -bench .
 	go test -bench .
 
 
 The complex benchmarks generate fake HTTP messages with the following regex:
 The complex benchmarks generate fake HTTP messages with the following regex:
+
 	POST (/[-a-zA-Z0-9_.]{3,12}){3,6}
 	POST (/[-a-zA-Z0-9_.]{3,12}){3,6}
 	Content-Length: [0-9]{2,3}
 	Content-Length: [0-9]{2,3}
 	X-Auth-Token: [a-zA-Z0-9+/]{64}
 	X-Auth-Token: [a-zA-Z0-9+/]{64}
@@ -79,12 +101,14 @@ The complex benchmarks generate fake HTTP messages with the following regex:
 	){3,15}[A-Za-z0-9+/]{60}([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)
 	){3,15}[A-Za-z0-9+/]{60}([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)
 
 
 The repetitive benchmarks use the regex
 The repetitive benchmarks use the regex
+
 	a{999}
 	a{999}
 
 
 See regen_benchmarks_test.go for more information.
 See regen_benchmarks_test.go for more information.
 
 
 On my mid-2014 MacBook Pro (2.6GHz Intel Core i5, 8GB 1600MHz DDR3),
 On my mid-2014 MacBook Pro (2.6GHz Intel Core i5, 8GB 1600MHz DDR3),
 the results of running the benchmarks with minimal load are:
 the results of running the benchmarks with minimal load are:
+
 	BenchmarkComplexCreation-4                       200	   8322160 ns/op
 	BenchmarkComplexCreation-4                       200	   8322160 ns/op
 	BenchmarkComplexGeneration-4                   10000	    153625 ns/op
 	BenchmarkComplexGeneration-4                   10000	    153625 ns/op
 	BenchmarkLargeRepeatCreateSerial-4  	        3000	    411772 ns/op
 	BenchmarkLargeRepeatCreateSerial-4  	        3000	    411772 ns/op
@@ -96,6 +120,7 @@ import (
 	"fmt"
 	"fmt"
 	"math/rand"
 	"math/rand"
 	"regexp/syntax"
 	"regexp/syntax"
+	"strings"
 )
 )
 
 
 // DefaultMaxUnboundedRepeatCount is default value for MaxUnboundedRepeatCount.
 // DefaultMaxUnboundedRepeatCount is default value for MaxUnboundedRepeatCount.
@@ -107,7 +132,7 @@ const DefaultMaxUnboundedRepeatCount = 4096
 // group is the regular expression within the group (e.g. for `(\w+)`, group would be `\w+`).
 // group is the regular expression within the group (e.g. for `(\w+)`, group would be `\w+`).
 // generator is the generator for group.
 // generator is the generator for group.
 // args is the args used to create the generator calling this function.
 // args is the args used to create the generator calling this function.
-type CaptureGroupHandler func(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) string
+type CaptureGroupHandler func(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) ([]byte, error)
 
 
 // GeneratorArgs are arguments passed to NewGenerator that control how generators
 // GeneratorArgs are arguments passed to NewGenerator that control how generators
 // are created.
 // are created.
@@ -131,6 +156,12 @@ type GeneratorArgs struct {
 	// from the expressions in the group.
 	// from the expressions in the group.
 	CaptureGroupHandler CaptureGroupHandler
 	CaptureGroupHandler CaptureGroupHandler
 
 
+	// Generates bytes instead of valid UTF-8 strings, default is false.
+	// If enabled any char "." will generate a byte in the range 0-255.
+	//
+	// ByteMode is not compatible with negated character classes (e.g. "[^a]").
+	ByteMode bool
+
 	// Used by generators.
 	// Used by generators.
 	rng *rand.Rand
 	rng *rand.Rand
 }
 }
@@ -175,29 +206,36 @@ func (a *GeneratorArgs) Rng() *rand.Rand {
 	return a.rng
 	return a.rng
 }
 }
 
 
-// Generator generates random strings.
+// Generator generates random bytes or strings.
 type Generator interface {
 type Generator interface {
-	Generate() string
+	Generate() ([]byte, error)
 	String() string
 	String() string
 }
 }
 
 
 /*
 /*
-Generate a random string that matches the regular expression pattern.
+GenerateString generates a random string that matches the regular expression pattern.
 If args is nil, default values are used.
 If args is nil, default values are used.
 
 
 This function does not seed the default RNG, so you must call rand.Seed() if you want
 This function does not seed the default RNG, so you must call rand.Seed() if you want
 non-deterministic strings.
 non-deterministic strings.
 */
 */
-func Generate(pattern string) (string, error) {
+func GenerateString(pattern string) (string, error) {
 	generator, err := NewGenerator(pattern, nil)
 	generator, err := NewGenerator(pattern, nil)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	return generator.Generate(), nil
+	b, err := generator.Generate()
+	return string(b), err
 }
 }
 
 
-// NewGenerator creates a generator that returns random strings that match the regular expression in pattern.
-// If args is nil, default values are used.
+// NewGenerator creates a generator that returns random strings that match the
+// regular expression in pattern. If args is nil, default values are used.
+//
+// If ByteMode is true, pattern should not contain negated character
+// classes (e.g. "[^a]"). This limitation is due to how synxtax.Parse handles
+// negated character classes, which is by replacing them with a positive
+// character range. This makes it impossible to infer the original negated
+// character class.
 func NewGenerator(pattern string, inputArgs *GeneratorArgs) (generator Generator, err error) {
 func NewGenerator(pattern string, inputArgs *GeneratorArgs) (generator Generator, err error) {
 	args := GeneratorArgs{}
 	args := GeneratorArgs{}
 
 
@@ -209,6 +247,25 @@ func NewGenerator(pattern string, inputArgs *GeneratorArgs) (generator Generator
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	if args.ByteMode {
+		negatedClasses := []string{
+			"[^",
+			"[[:^",
+			`\P`,
+			`\D`,
+			`\S`,
+			`\W`,
+		}
+		for _, negatedCls := range negatedClasses {
+			if strings.Contains(pattern, negatedCls) {
+				return nil, generatorError(nil, "negated character classes are not supported")
+			}
+		}
+		if strings.Contains(pattern, `\x{`) {
+			return nil, generatorError(nil, "only two digit hex codes are supported in byte mode")
+		}
+	}
+
 	var regexp *syntax.Regexp
 	var regexp *syntax.Regexp
 	regexp, err = syntax.Parse(pattern, args.Flags)
 	regexp, err = syntax.Parse(pattern, args.Flags)
 	if err != nil {
 	if err != nil {

+ 85 - 0
psiphon/common/regen/regen_benchmarks_test.go

@@ -0,0 +1,85 @@
+/*
+Copyright 2014 Zachary Klippenstein
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package regen
+
+import (
+	"math/rand"
+	"testing"
+)
+
+const BigFancyRegexp = `
+POST (/[-a-zA-Z0-9_.]{3,12}){3,6}
+Content-Length: [0-9]{2,3}
+X-Auth-Token: [a-zA-Z0-9+/]{64}
+
+([A-Za-z0-9+/]{64}
+){3,15}[A-Za-z0-9+/]{60}([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)
+`
+
+var rngSource = rand.NewSource(42)
+
+// Benchmarks the code that creates generators.
+// Doesn't actually run the generators.
+func BenchmarkComplexCreation(b *testing.B) {
+	// Create everything here to save allocations in the loop.
+	//args := &GeneratorArgs{rngSource, 0, NewSerialExecutor()}
+	args := &GeneratorArgs{
+		RngSource: rngSource,
+		Flags:     0,
+	}
+
+	for i := 0; i < b.N; i++ {
+		NewGenerator(BigFancyRegexp, args)
+	}
+}
+
+func BenchmarkLargeRepeatCreateSerial(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		NewGenerator(`a{999}`, &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		})
+	}
+}
+
+func BenchmarkComplexGeneration(b *testing.B) {
+	args := &GeneratorArgs{
+		RngSource: rngSource,
+	}
+	generator, err := NewGenerator(BigFancyRegexp, args)
+	if err != nil {
+		panic(err)
+	}
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		generator.Generate()
+	}
+}
+
+func BenchmarkLargeRepeatGenerateSerial(b *testing.B) {
+	generator, err := NewGenerator(`a{999}`, &GeneratorArgs{
+		RngSource: rand.NewSource(0),
+	})
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		generator.Generate()
+	}
+}

+ 935 - 0
psiphon/common/regen/regen_test.go

@@ -0,0 +1,935 @@
+/*
+Copyright 2014 Zachary Klippenstein
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+ * Copyright (c) 2023, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package regen
+
+import (
+	"encoding/hex"
+	"fmt"
+	"math"
+	"math/rand"
+	"os"
+	"regexp"
+	"regexp/syntax"
+	"strings"
+	"testing"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
+)
+
+const (
+	// Each expression is generated and validated this many times.
+	SampleSize = 999
+
+	// Arbitrary limit in the standard package.
+	// See https://golang.org/src/regexp/syntax/parse.go?s=18885:18935#L796
+	MaxSupportedRepeatCount = 1000
+)
+
+func ExampleGenerate() {
+	pattern := "[ab]{5}"
+	bytes, _ := GenerateString(pattern)
+
+	if matched, _ := regexp.MatchString(pattern, string(bytes)); matched {
+		fmt.Println("Matches!")
+	}
+
+	// Output:
+	// Matches!
+}
+
+func ExampleNewGenerator() {
+	pattern := "[ab]{5}"
+
+	// Note that this uses a constant seed, so the generated string
+	// will always be the same across different runs of the program.
+	// Use a more random seed for real use (e.g. time-based).
+	generator, _ := NewGenerator(pattern, &GeneratorArgs{
+		RngSource: rand.NewSource(0),
+	})
+
+	bytes, err := generator.Generate()
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	if matched, _ := regexp.MatchString(pattern, string(bytes)); matched {
+		fmt.Println("Matches!")
+	}
+
+	// Output:
+	// Matches!
+}
+
+func ExampleByteModeGenerator() {
+	for i := 0; i < 100; i++ {
+		gen, err := NewGenerator(`([\x00-\x6a])\x00\x01\x02[\x00-\xff]{5}`, &GeneratorArgs{
+			ByteMode: true,
+		})
+		if err != nil {
+			panic(err)
+		}
+		x, err := gen.Generate()
+		if err != nil {
+			panic(err)
+		}
+		fmt.Println(hex.EncodeToString(x))
+	}
+}
+
+func ExampleNewGenerator_perl() {
+	pattern := `\d{5}`
+
+	generator, _ := NewGenerator(pattern, &GeneratorArgs{
+		Flags: syntax.Perl,
+	})
+
+	bytes, err := generator.Generate()
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	if matched, _ := regexp.MatchString("[[:digit:]]{5}", string(bytes)); matched {
+		fmt.Println("Matches!")
+	}
+	// Output:
+	// Matches!
+}
+
+func ExampleCaptureGroupHandler() {
+	pattern := `Hello, (?P<firstname>[A-Z][a-z]{2,10}) (?P<lastname>[A-Z][a-z]{2,10})`
+
+	generator, _ := NewGenerator(pattern, &GeneratorArgs{
+		Flags: syntax.Perl,
+		CaptureGroupHandler: func(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) ([]byte, error) {
+			value, err := generator.Generate()
+			if err != nil {
+				return nil, err
+			}
+			if name == "firstname" {
+				return []byte(fmt.Sprintf("FirstName (e.g. %s)", string(value))), nil
+			}
+			return []byte(fmt.Sprintf("LastName (e.g. %s)", string(value))), nil
+		},
+	})
+
+	// Print to stderr since we're generating random output and can't assert equality.
+	value, err := generator.Generate()
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	fmt.Fprintln(os.Stderr, value)
+
+	// Needed for "go test" to run this example. (Must be a blank line before.)
+
+	// Output:
+}
+
+func TestGeneratorArgs(t *testing.T) {
+	t.Parallel()
+
+	t.Run("Handle empty struct", func(t *testing.T) {
+		shouldNotPanic(t, func() {
+			args := GeneratorArgs{}
+
+			err := args.initialize()
+			if err != nil {
+				t.Fatal(err)
+			}
+		})
+	})
+
+	t.Run("Unicode groups not supported", func(t *testing.T) {
+		args := &GeneratorArgs{
+			Flags: syntax.UnicodeGroups,
+		}
+
+		err := args.initialize()
+		if err == nil {
+			t.Fatal("expected error")
+		}
+		if err.Error() != "UnicodeGroups not supported" {
+			t.Fatalf("unexpected error: %v", err)
+		}
+	})
+
+	t.Run("Panics if repeat bounds are invalid", func(t *testing.T) {
+		args := &GeneratorArgs{
+			MinUnboundedRepeatCount: 2,
+			MaxUnboundedRepeatCount: 1,
+		}
+
+		shouldPanicWith(t, func() {
+			_ = args.initialize()
+		}, "MinUnboundedRepeatCount(2) > MaxUnboundedRepeatCount(1)")
+	})
+
+	t.Run("Allow equal repeat bounds", func(t *testing.T) {
+		args := &GeneratorArgs{
+			MinUnboundedRepeatCount: 1,
+			MaxUnboundedRepeatCount: 1,
+		}
+
+		shouldNotPanic(t, func() {
+			err := args.initialize()
+			if err != nil {
+				t.Fatal(err)
+			}
+		})
+	})
+
+	t.Run("Rng", func(t *testing.T) {
+
+		t.Run("Panics if called before initialize", func(t *testing.T) {
+			args := &GeneratorArgs{}
+
+			shouldPanic(t, func() {
+				_ = args.Rng()
+			})
+		})
+
+		t.Run("Non-nil after initialize", func(t *testing.T) {
+			args := GeneratorArgs{}
+			err := args.initialize()
+			if err != nil {
+				t.Fatal(err)
+			}
+			rng := args.Rng()
+			if rng == nil {
+				t.Fatal("expected non-nil")
+			}
+		})
+
+	})
+}
+
+func TestNewGenerator(t *testing.T) {
+	t.Parallel()
+
+	t.Run("Handles nil GeneratorArgs", func(t *testing.T) {
+		generator, err := NewGenerator("", nil)
+		if generator == nil {
+			t.Fatal("expected non-nil")
+		}
+		if err != nil {
+			t.Fatal(err)
+		}
+	})
+
+	t.Run("Handles empty GeneratorArgs", func(t *testing.T) {
+		generator, err := NewGenerator("", &GeneratorArgs{})
+		if generator == nil {
+			t.Fatal("expected non-nil")
+		}
+		if err != nil {
+			t.Fatal(err)
+		}
+	})
+
+	t.Run("Forwards errors from arsg initialization", func(t *testing.T) {
+		args := &GeneratorArgs{
+			Flags: syntax.UnicodeGroups,
+		}
+
+		_, err := NewGenerator("", args)
+		if err == nil {
+			t.Fatal("expected error")
+		}
+	})
+}
+
+func TestGenEmpty(t *testing.T) {
+	t.Parallel()
+
+	args := &GeneratorArgs{
+		RngSource: rand.NewSource(0),
+	}
+
+	testGeneratesStringMatching(t, args, "", "^$")
+}
+
+func TestGenLiterals(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil,
+		"a",
+		"abc",
+	)
+}
+
+func TestGenDotNotNl(t *testing.T) {
+	t.Parallel()
+
+	t.Run("DotNotNl", func(t *testing.T) {
+		testGeneratesStringMatchingItself(t, nil, ".")
+	})
+
+	t.Run("No newlines are generated", func(t *testing.T) {
+		generator, _ := NewGenerator(".", nil)
+
+		// Not a very strong assertion, but not sure how to do better. Exploring the entire
+		// generation space (2^32) takes far too long for a unit test.
+		for i := 0; i < SampleSize; i++ {
+			value, err := generator.Generate()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if strings.Contains(string(value), "\n") {
+				t.Fatalf("unexpected newline in %q", value)
+			}
+		}
+	})
+
+}
+
+func TestGenStringStartEnd(t *testing.T) {
+	t.Parallel()
+
+	args := &GeneratorArgs{
+		RngSource: rand.NewSource(0),
+		Flags:     0,
+	}
+
+	testGeneratesStringMatching(t, args, "^abc$", "^abc$")
+	testGeneratesStringMatching(t, args, "$abc^", "^abc$")
+	testGeneratesStringMatching(t, args, "a^b$c", "^abc$")
+}
+
+func TestGenQuestionMark(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil,
+		"a?",
+		"(abc)?",
+		"[ab]?",
+		".?",
+	)
+}
+
+func TestGenPlus(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil, "a+")
+}
+
+func TestGenStar(t *testing.T) {
+	t.Parallel()
+
+	t.Run("HitsDefaultMin", func(t *testing.T) {
+		regexp := "a*"
+		args := &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		}
+		counts := generateLenHistogram(regexp, DefaultMaxUnboundedRepeatCount, args)
+
+		if counts[0] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+	})
+
+	t.Run("HitsCustomMin", func(t *testing.T) {
+		regexp := "a*"
+		args := &GeneratorArgs{
+			RngSource:               rand.NewSource(0),
+			MinUnboundedRepeatCount: 200,
+		}
+		counts := generateLenHistogram(regexp, DefaultMaxUnboundedRepeatCount, args)
+
+		if counts[200] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+		for i := 0; i < 200; i++ {
+			if counts[i] != 0 {
+				t.Fatalf("count should be 0")
+			}
+		}
+	})
+
+	t.Run("HitsDefaultMax", func(t *testing.T) {
+		regexp := "a*"
+		args := &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		}
+		counts := generateLenHistogram(regexp, DefaultMaxUnboundedRepeatCount, args)
+
+		if len(counts) != DefaultMaxUnboundedRepeatCount+1 {
+			t.Fatalf("count should be %d", DefaultMaxUnboundedRepeatCount+1)
+		}
+		if counts[DefaultMaxUnboundedRepeatCount] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+	})
+
+	t.Run("HitsCustomMax", func(t *testing.T) {
+		regexp := "a*"
+		args := &GeneratorArgs{
+			RngSource:               rand.NewSource(0),
+			MaxUnboundedRepeatCount: 200,
+		}
+		counts := generateLenHistogram(regexp, 200, args)
+
+		if len(counts) != 201 {
+			t.Fatalf("count should be 201")
+		}
+		if counts[200] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+	})
+}
+
+func TestGenCharClassNotNl(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil,
+		"[a]",
+		"[abc]",
+		"[a-d]",
+		"[ac]",
+		"[0-9]",
+		"[a-z0-9]",
+	)
+
+	t.Run("No newlines are generated", func(t *testing.T) {
+
+		generator, _ := NewGenerator("[^a-zA-Z0-9]", nil)
+		for i := 0; i < SampleSize; i++ {
+			value, err := generator.Generate()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if strings.Contains(string(value), "\n") {
+				t.Fatalf("unexpected newline in %q", value)
+			}
+		}
+
+	})
+
+}
+
+func TestGenNegativeCharClass(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil, "[^a-zA-Z0-9]")
+}
+
+func TestGenAlternative(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil,
+		"a|b",
+		"abc|def|ghi",
+		"[ab]|[cd]",
+		"foo|bar|baz", // rewrites to foo|ba[rz]
+	)
+}
+
+func TestGenCapture(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatching(t, nil, "(abc)", "^abc$")
+	testGeneratesStringMatching(t, nil, "(a)(b)(c)", "^abc$")
+	testGeneratesStringMatching(t, nil, "()", "^$")
+}
+
+func TestGenConcat(t *testing.T) {
+	t.Parallel()
+
+	testGeneratesStringMatchingItself(t, nil, "[ab][cd]")
+}
+
+func TestGenRepeat(t *testing.T) {
+	t.Parallel()
+
+	t.Run("Unbounded", func(t *testing.T) {
+		testGeneratesStringMatchingItself(t, nil, `a{1,}`)
+
+		t.Run("HitsDefaultMax", func(t *testing.T) {
+			regexp := "a{0,}"
+			args := &GeneratorArgs{
+				RngSource: rand.NewSource(0),
+			}
+			counts := generateLenHistogram(regexp, DefaultMaxUnboundedRepeatCount, args)
+
+			if len(counts) != DefaultMaxUnboundedRepeatCount+1 {
+				t.Fatalf("count should be %d", DefaultMaxUnboundedRepeatCount+1)
+			}
+			if counts[DefaultMaxUnboundedRepeatCount] == 0 {
+				t.Fatalf("count should be > 0")
+			}
+		})
+
+		t.Run("HitsCustomMax", func(t *testing.T) {
+			regexp := "a{0,}"
+			args := &GeneratorArgs{
+				RngSource:               rand.NewSource(0),
+				MaxUnboundedRepeatCount: 200,
+			}
+			counts := generateLenHistogram(regexp, 200, args)
+
+			if len(counts) != 201 {
+				t.Fatalf("count should be 201")
+			}
+			if counts[200] == 0 {
+				t.Fatalf("count should be > 0")
+			}
+		})
+	})
+
+	t.Run("HitsMin", func(t *testing.T) {
+		regexp := "a{0,3}"
+		args := &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		}
+		counts := generateLenHistogram(regexp, 3, args)
+
+		if len(counts) != 4 {
+			t.Fatalf("count should be 4")
+		}
+		if counts[0] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+	})
+
+	t.Run("HitsMax", func(t *testing.T) {
+		regexp := "a{0,3}"
+		args := &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		}
+		counts := generateLenHistogram(regexp, 3, args)
+
+		if len(counts) != 4 {
+			t.Fatalf("count should be 4")
+		}
+		if counts[3] == 0 {
+			t.Fatalf("count should be > 0")
+		}
+	})
+
+	t.Run("IsWithinBounds", func(t *testing.T) {
+		regexp := "a{5,10}"
+		args := &GeneratorArgs{
+			RngSource: rand.NewSource(0),
+		}
+		counts := generateLenHistogram(regexp, 10, args)
+
+		if len(counts) != 11 {
+			t.Fatalf("count should be 11")
+		}
+
+		for i := 0; i < 11; i++ {
+			if i < 5 {
+				if counts[i] != 0 {
+					t.Fatalf("count should be 0")
+				}
+			} else if i < 11 {
+				if counts[i] == 0 {
+					t.Fatalf("count should be > 0")
+				}
+			}
+		}
+	})
+
+}
+
+func TestGenCharClasses(t *testing.T) {
+	t.Parallel()
+
+	t.Run("Ascii", func(t *testing.T) {
+		testGeneratesStringMatchingItself(t, nil,
+			"[[:alnum:]]",
+			"[[:alpha:]]",
+			"[[:ascii:]]",
+			"[[:blank:]]",
+			"[[:cntrl:]]",
+			"[[:digit:]]",
+			"[[:graph:]]",
+			"[[:lower:]]",
+			"[[:print:]]",
+			"[[:punct:]]",
+			"[[:space:]]",
+			"[[:upper:]]",
+			"[[:word:]]",
+			"[[:xdigit:]]",
+			"[[:^alnum:]]",
+			"[[:^alpha:]]",
+			"[[:^ascii:]]",
+			"[[:^blank:]]",
+			"[[:^cntrl:]]",
+			"[[:^digit:]]",
+			"[[:^graph:]]",
+			"[[:^lower:]]",
+			"[[:^print:]]",
+			"[[:^punct:]]",
+			"[[:^space:]]",
+			"[[:^upper:]]",
+			"[[:^word:]]",
+			"[[:^xdigit:]]",
+		)
+	})
+
+	t.Run("Perl", func(t *testing.T) {
+		args := &GeneratorArgs{
+			Flags: syntax.Perl,
+		}
+
+		testGeneratesStringMatchingItself(t, args,
+			`\d`,
+			`\s`,
+			`\w`,
+			`\D`,
+			`\S`,
+			`\W`,
+		)
+	})
+}
+
+func TestCaptureGroupHandler(t *testing.T) {
+	t.Parallel()
+
+	callCount := 0
+
+	gen, err := NewGenerator(`(?:foo) (bar) (?P<name>baz)`, &GeneratorArgs{
+		Flags: syntax.PerlX,
+		CaptureGroupHandler: func(index int, name string, group *syntax.Regexp, generator Generator, args *GeneratorArgs) ([]byte, error) {
+			callCount++
+
+			if index >= 2 {
+				t.Fatalf("index should be < 2")
+			}
+
+			if index == 0 {
+				if name != "" {
+					t.Fatalf("name should be empty")
+				}
+				if group.String() != "bar" {
+					t.Fatalf("group should be 'bar'")
+				}
+				value, err := generator.Generate()
+				if err != nil {
+					t.Fatalf("err should be nil")
+				}
+				if string(value) != "bar" {
+					t.Fatalf("value should be 'bar'")
+				}
+				return []byte("one"), nil
+			}
+
+			// Index 1
+			if name != "name" {
+				t.Fatalf("name should be 'name'")
+			}
+			if group.String() != "baz" {
+				t.Fatalf("group should be 'baz'")
+			}
+			value, err := generator.Generate()
+			if err != nil {
+				t.Fatalf("err should be nil")
+			}
+			if string(value) != "baz" {
+				t.Fatalf("value should be 'baz'")
+			}
+			return []byte("two"), nil
+		},
+	})
+	if err != nil {
+		t.Fatalf("err should be nil")
+	}
+
+	value, _ := gen.Generate()
+
+	if string(value) != "foo one two" {
+		t.Fatalf("value should be 'foo one two'")
+	}
+	if callCount != 2 {
+		t.Fatalf("callCount should be 2")
+	}
+}
+
+// Byte mode tests
+
+func TestByteModeUniform(t *testing.T) {
+	t.Parallel()
+
+	type test struct {
+		name         string
+		pattern      string
+		length       int   // length of generated bytes
+		uniformRange []int // [min, max] byte values expected to be unfiformly generated
+		flags        syntax.Flags
+	}
+
+	tests := []test{
+		{name: "any byte not NL", pattern: ".", length: 1},
+		{name: "any byte", pattern: ".", length: 1, flags: syntax.MatchNL},
+		{name: "class range", pattern: `[\x00-\xff]`, length: 1},
+		{name: "class multi range", pattern: `[\x00-\x7f\x80-\xff]`, length: 1},
+		{name: "grouping", pattern: `([\x00-\xff])`, length: 1},
+		{name: "empty strings", pattern: `^[\x00-\xff]$`, length: 1},
+		{name: "exactly 1", pattern: `[\x00-\xff]{1}`, length: 1},
+		{name: "exactly 10", pattern: `[\x00-\xff]{10}`, length: 10},
+		{name: "repetition 1", pattern: `[\x00-\xff]{1,1}`, length: 1},
+		{name: "alteration", pattern: `([[:ascii:]]|[\x80-\xff])`, length: 1},
+		{
+			name:         "printable ascii",
+			pattern:      `[[:print:]]`,
+			length:       1,
+			uniformRange: []int{' ', '~'},
+		},
+		{
+			name:         "digits",
+			pattern:      `[0-9]{5}`,
+			length:       5,
+			uniformRange: []int{'0', '9'},
+		},
+		{
+			name:         "digits ascii char class",
+			pattern:      `[[:digit:]]`,
+			length:       1,
+			uniformRange: []int{'0', '9'},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+
+			buckets := make([]int, 256)
+			iters := 200_000
+
+			rng, err := prng.NewPRNG()
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			gen, err := NewGenerator(tt.pattern, &GeneratorArgs{
+				RngSource: rng,
+				Flags:     tt.flags,
+				ByteMode:  true,
+			})
+
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			for i := 0; i < iters; i++ {
+				value, err := gen.Generate()
+				if err != nil {
+					t.Fatal(err)
+				}
+				if len(value) != tt.length {
+					t.Fatalf("expected length %d, got %d", tt.length, len(value))
+				}
+				for _, b := range value {
+					buckets[int(b)]++
+				}
+			}
+
+			uniformRange := []int{0, 255}
+			if tt.uniformRange != nil {
+				if len(tt.uniformRange) != 2 {
+					t.Fatal("expected uniformRange to be a slice of length 2")
+				}
+				uniformRange = tt.uniformRange
+			}
+
+			// Checks if generated bytes are uniformly distributed across
+			// the buckets in the range uniformBuckets[0] to uniformBuckets[1].
+			expectedCount := iters * tt.length / (uniformRange[1] - uniformRange[0] + 1)
+			if !isUniform(buckets[uniformRange[0]:uniformRange[1]+1], expectedCount) {
+				t.Fatalf("expected uniform distribution: %v", buckets[uniformRange[0]:uniformRange[1]+1])
+			}
+		})
+	}
+
+}
+
+func TestByteModeNegatedClasses(t *testing.T) {
+	t.Parallel()
+
+	patterns := []string{
+		"[^0-9]",
+		"\\P",
+		"\\D",
+		"\\S",
+		"\\W",
+		"[^[:ascii:]]",
+		"[[:^ascii:]]",
+	}
+
+	errStr := "negated character classes are not supported"
+
+	for _, pattern := range patterns {
+		gen, err := NewGenerator(pattern, &GeneratorArgs{
+			ByteMode: true,
+		})
+		if gen != nil {
+			t.Fatalf("expected error for %s", pattern)
+		}
+		if err.Error() != errStr {
+			t.Fatalf("expected error %q, got %q", errStr, err.Error())
+		}
+	}
+}
+
+func testGeneratesStringMatchingItself(t *testing.T, args *GeneratorArgs, patterns ...string) {
+	t.Helper()
+	for _, pattern := range patterns {
+		t.Run(fmt.Sprintf("String generated from /%s/ matches itself", pattern), func(t *testing.T) {
+			err := shouldGenerateStringMatching(pattern, pattern, args)
+			if err != nil {
+				t.Fatal(err)
+			}
+		})
+	}
+}
+
+func testGeneratesStringMatching(t *testing.T, args *GeneratorArgs, pattern, expectedPattern string) {
+	t.Helper()
+	t.Run(fmt.Sprintf("String generated from /%s/ matches /%s/", pattern, expectedPattern), func(t *testing.T) {
+		err := shouldGenerateStringMatching(pattern, expectedPattern, args)
+		if err != nil {
+			t.Fatal(err)
+		}
+	})
+}
+
+func shouldGenerateStringMatching(pattern, expectedPattern string, args *GeneratorArgs) error {
+	return shouldGenerateStringMatchingTimes(pattern, expectedPattern, args, SampleSize)
+}
+
+func shouldGenerateStringMatchingTimes(pattern, expectedPattern string, args *GeneratorArgs, times int) error {
+
+	generator, err := NewGenerator(pattern, args)
+	if err != nil {
+		panic(err)
+	}
+
+	for i := 0; i < times; i++ {
+		result, err := generator.Generate()
+		if err != nil {
+			panic(err)
+		}
+		matched, err := regexp.MatchString(expectedPattern, string(result))
+		if err != nil {
+			panic(err)
+		}
+		if !matched {
+			return fmt.Errorf("string “%s” generated from /%s/ did not match /%s/.",
+				result, pattern, expectedPattern)
+		}
+	}
+
+	return nil
+}
+
+func generateLenHistogram(regexp string, maxLen int, args *GeneratorArgs) (counts []int) {
+	generator, err := NewGenerator(regexp, args)
+	if err != nil {
+		panic(err)
+	}
+
+	iterations := max(maxLen*4, SampleSize)
+
+	for i := 0; i < iterations; i++ {
+		value, err := generator.Generate()
+		if err != nil {
+			panic(err)
+		}
+		str := string(value)
+		// Grow the slice if necessary.
+		if len(str) >= len(counts) {
+			newCounts := make([]int, len(str)+1)
+			copy(newCounts, counts)
+			counts = newCounts
+		}
+
+		counts[len(str)]++
+	}
+
+	return
+}
+
+// isUnifrom performs a chi-squared test with 0.025 significance.
+// Each bucket in xs is compared against the expected_value.
+func isUniform(xs []int, expected_value int) bool {
+	critical_squared := float64(25.24) // = 5.024 ^ 2 at 0.025
+	for _, x := range xs {
+		chi_squared := math.Pow(float64(x-expected_value), 2) / float64(expected_value)
+		if chi_squared > critical_squared {
+			return false
+		}
+	}
+	return true
+}
+
+func max(values ...int) int {
+	m := values[0]
+	for _, v := range values {
+		if v > m {
+			m = v
+		}
+	}
+	return m
+}
+
+func shouldPanic(t *testing.T, f func()) {
+	t.Helper()
+	defer func() { _ = recover() }()
+	f()
+	t.Errorf("should have panicked")
+}
+
+func shouldPanicWith(t *testing.T, f func(), expected string) {
+	t.Helper()
+	defer func() {
+		if r := recover(); r != expected {
+			t.Errorf("expected panic %q, got %q", expected, r)
+		}
+	}()
+	f()
+	t.Errorf("should have panicked")
+}
+
+func shouldNotPanic(t *testing.T, f func()) {
+	t.Helper()
+	defer func() {
+		if r := recover(); r != nil {
+			t.Error("should not have panicked")
+		}
+	}()
+	f()
+}

+ 34 - 34
vendor/github.com/zach-klippenstein/goregen/regexp_format.go → psiphon/common/regen/regexp_format.go

@@ -14,6 +14,25 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 */
 */
 
 
+/*
+ * Copyright (c) 2023, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
 package regen
 package regen
 
 
 import (
 import (
@@ -30,20 +49,6 @@ func inspectRegexpToString(r *syntax.Regexp) string {
 	return buffer.String()
 	return buffer.String()
 }
 }
 
 
-// inspectPatternsToString returns a string describing one or more regular expressions.
-func inspectPatternsToString(simplify bool, patterns ...string) string {
-	var buffer bytes.Buffer
-	for _, pattern := range patterns {
-		inspectPatternsToWriter(simplify, &buffer, pattern)
-	}
-	return buffer.String()
-}
-func inspectPatternsToWriter(simplify bool, w io.Writer, patterns ...string) {
-	for _, pattern := range patterns {
-		inspectRegexpToWriter(w, parseOrPanic(simplify, pattern))
-	}
-}
-
 func inspectRegexpToWriter(w io.Writer, r ...*syntax.Regexp) {
 func inspectRegexpToWriter(w io.Writer, r ...*syntax.Regexp) {
 	for _, regexp := range r {
 	for _, regexp := range r {
 		inspectWithIndent(regexp, "", w)
 		inspectWithIndent(regexp, "", w)
@@ -63,37 +68,32 @@ func inspectWithIndent(r *syntax.Regexp, indent string, w io.Writer) {
 	} else {
 	} else {
 		fmt.Fprintf(w, "%s  Sub: []\n", indent)
 		fmt.Fprintf(w, "%s  Sub: []\n", indent)
 	}
 	}
-	fmt.Fprintf(w, "%s  Rune: %s (%s)\n", indent, runesToString(r.Rune...), runesToDecimalString(r.Rune))
+	fmt.Fprintf(w, "%s  Rune: %s (%s)\n", indent, runesToUTF8(r.Rune...), runesToDecimalString(r.Rune))
 	fmt.Fprintf(w, "%s  [Min, Max]: [%d, %d]\n", indent, r.Min, r.Max)
 	fmt.Fprintf(w, "%s  [Min, Max]: [%d, %d]\n", indent, r.Min, r.Max)
 	fmt.Fprintf(w, "%s  Cap: %d\n", indent, r.Cap)
 	fmt.Fprintf(w, "%s  Cap: %d\n", indent, r.Cap)
 	fmt.Fprintf(w, "%s  Name: %s\n", indent, r.Name)
 	fmt.Fprintf(w, "%s  Name: %s\n", indent, r.Name)
 }
 }
 
 
-// ParseOrPanic parses a regular expression into an AST.
-// Panics on error.
-func parseOrPanic(simplify bool, pattern string) *syntax.Regexp {
-	regexp, err := syntax.Parse(pattern, 0)
-	if err != nil {
-		panic(err)
-	}
-	if simplify {
-		regexp = regexp.Simplify()
+// runesToUTF8 converts a slice of runes to the Unicode string they represent.
+func runesToUTF8(runes ...rune) []byte {
+	var buffer bytes.Buffer
+	for _, r := range runes {
+		buffer.WriteRune(r)
 	}
 	}
-	return regexp
+	return buffer.Bytes()
 }
 }
 
 
-// runesToString converts a slice of runes to the string they represent.
-func runesToString(runes ...rune) string {
-	defer func() {
-		if err := recover(); err != nil {
-			panic(fmt.Errorf("RunesToString panicked"))
-		}
-	}()
+// runesToBytes converst a slice of runes to a slice of bytes.
+// Returns an error if runes not in the range [0-255].
+func runesToBytes(runes ...rune) ([]byte, error) {
 	var buffer bytes.Buffer
 	var buffer bytes.Buffer
 	for _, r := range runes {
 	for _, r := range runes {
-		buffer.WriteRune(r)
+		if r < 0 || r > 255 {
+			return nil, fmt.Errorf("RunesToBytes: rune out of range")
+		}
+		buffer.WriteByte(byte(r))
 	}
 	}
-	return buffer.String()
+	return buffer.Bytes(), nil
 }
 }
 
 
 // RunesToDecimalString converts a slice of runes to their comma-separated decimal values.
 // RunesToDecimalString converts a slice of runes to their comma-separated decimal values.

+ 0 - 0
vendor/github.com/zach-klippenstein/goregen/rng.go → psiphon/common/regen/rng.go


+ 47 - 0
psiphon/common/regen/rng_test.go

@@ -0,0 +1,47 @@
+/*
+Copyright 2014 Zachary Klippenstein
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package regen
+
+import "testing"
+
+func TestXorShift64(t *testing.T) {
+	t.Run("Int63 should never return negative numbers", func(t *testing.T) {
+		source := xorShift64Source(1)
+		for i := 0; i < SampleSize; i++ {
+			val := source.Int63()
+
+			if val < 0 {
+				t.Fatal("Int63 returned a negative number")
+			}
+		}
+	})
+
+	t.Run("Should not only return zeros", func(t *testing.T) {
+		source := xorShift64Source(0)
+		nonZeroCount := 0
+
+		for i := 0; i < SampleSize; i++ {
+			if source.Int63() != 0 {
+				nonZeroCount++
+			}
+		}
+
+		if nonZeroCount <= 0 {
+			t.Fatal("Int63 returned non-positive numbers")
+		}
+	})
+}

+ 10 - 7
psiphon/common/transforms/transforms.go

@@ -29,7 +29,7 @@ import (
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
-	regen "github.com/zach-klippenstein/goregen"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/regen"
 )
 )
 
 
 const (
 const (
@@ -155,7 +155,7 @@ func (spec Spec) ApplyString(seed *prng.Seed, input string) (string, error) {
 		if err != nil {
 		if err != nil {
 			return "", errors.Trace(err)
 			return "", errors.Trace(err)
 		}
 		}
-		value = re.ReplaceAllString(value, replacement)
+		value = re.ReplaceAllString(value, string(replacement))
 	}
 	}
 	return value, nil
 	return value, nil
 }
 }
@@ -173,7 +173,7 @@ func (spec Spec) Apply(seed *prng.Seed, input []byte) ([]byte, error) {
 		if err != nil {
 		if err != nil {
 			return nil, errors.Trace(err)
 			return nil, errors.Trace(err)
 		}
 		}
-		value = re.ReplaceAll(value, []byte(replacement))
+		value = re.ReplaceAll(value, replacement)
 	}
 	}
 	return value, nil
 	return value, nil
 }
 }
@@ -181,7 +181,7 @@ func (spec Spec) Apply(seed *prng.Seed, input []byte) ([]byte, error) {
 // makeRegexAndRepl generates the regex and replacement for a given seed and
 // makeRegexAndRepl generates the regex and replacement for a given seed and
 // transform. The same seed can be supplied to produce the same output, for
 // transform. The same seed can be supplied to produce the same output, for
 // replay.
 // replay.
-func makeRegexAndRepl(seed *prng.Seed, transform [2]string) (*regexp.Regexp, string, error) {
+func makeRegexAndRepl(seed *prng.Seed, transform [2]string) (*regexp.Regexp, []byte, error) {
 
 
 	// TODO: the compiled regexp and regen could be cached, but the seed is an
 	// TODO: the compiled regexp and regen could be cached, but the seed is an
 	// issue with caching the regen.
 	// issue with caching the regen.
@@ -192,14 +192,17 @@ func makeRegexAndRepl(seed *prng.Seed, transform [2]string) (*regexp.Regexp, str
 	}
 	}
 	rg, err := regen.NewGenerator(transform[1], args)
 	rg, err := regen.NewGenerator(transform[1], args)
 	if err != nil {
 	if err != nil {
-		return nil, "", errors.Trace(err)
+		return nil, nil, errors.Trace(err)
 	}
 	}
 
 
-	replacement := rg.Generate()
+	replacement, err := rg.Generate()
+	if err != nil {
+		return nil, nil, errors.Trace(err)
+	}
 
 
 	re, err := regexp.Compile(transform[0])
 	re, err := regexp.Compile(transform[0])
 	if err != nil {
 	if err != nil {
-		return nil, "", errors.Trace(err)
+		return nil, nil, errors.Trace(err)
 	}
 	}
 
 
 	return re, replacement, nil
 	return re, replacement, nil

+ 13 - 5
psiphon/common/values/values.go

@@ -18,10 +18,8 @@
  */
  */
 
 
 /*
 /*
-
 Package values provides a mechanism for specifying and selecting dynamic
 Package values provides a mechanism for specifying and selecting dynamic
 values employed by the Psiphon client and server.
 values employed by the Psiphon client and server.
-
 */
 */
 package values
 package values
 
 
@@ -36,7 +34,7 @@ import (
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
-	regen "github.com/zach-klippenstein/goregen"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/regen"
 	"golang.org/x/crypto/nacl/secretbox"
 	"golang.org/x/crypto/nacl/secretbox"
 )
 )
 
 
@@ -265,6 +263,8 @@ func GetContentType(PRNG *prng.PRNG) string {
 	return spec.GetValue(PRNG)
 	return spec.GetValue(PRNG)
 }
 }
 
 
+// generate string given the regexp pattern.
+// generate is intended to be used with hardcoded inputs, and panics on error.
 func generate(PRNG *prng.PRNG, pattern string) string {
 func generate(PRNG *prng.PRNG, pattern string) string {
 
 
 	args := &regen.GeneratorArgs{
 	args := &regen.GeneratorArgs{
@@ -275,7 +275,11 @@ func generate(PRNG *prng.PRNG, pattern string) string {
 	if err != nil {
 	if err != nil {
 		panic(err.Error())
 		panic(err.Error())
 	}
 	}
-	return rg.Generate()
+	value, err := rg.Generate()
+	if err != nil {
+		panic(err.Error())
+	}
+	return string(value)
 }
 }
 
 
 var (
 var (
@@ -337,7 +341,11 @@ func generateUserAgent() string {
 
 
 	g := userAgentGenerators[prng.Range(0, len(userAgentGenerators)-1)]
 	g := userAgentGenerators[prng.Range(0, len(userAgentGenerators)-1)]
 
 
-	value := g.generator.Generate()
+	bytes, err := g.generator.Generate()
+	if err != nil {
+		panic(err.Error())
+	}
+	value := string(bytes)
 	value = strings.ReplaceAll(value, "__VER__", g.version())
 	value = strings.ReplaceAll(value, "__VER__", g.version())
 	return value
 	return value
 }
 }

+ 3 - 3
psiphon/dialParameters.go

@@ -37,11 +37,11 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/regen"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/resolver"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/resolver"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/transforms"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/transforms"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/values"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/values"
 	utls "github.com/refraction-networking/utls"
 	utls "github.com/refraction-networking/utls"
-	regen "github.com/zach-klippenstein/goregen"
 	"golang.org/x/net/bpf"
 	"golang.org/x/net/bpf"
 )
 )
 
 
@@ -1326,7 +1326,7 @@ func selectFrontingParameters(
 		// Generate a front address based on the regex.
 		// Generate a front address based on the regex.
 
 
 		var err error
 		var err error
-		frontingDialHost, err = regen.Generate(serverEntry.MeekFrontingAddressesRegex)
+		frontingDialHost, err = regen.GenerateString(serverEntry.MeekFrontingAddressesRegex)
 		if err != nil {
 		if err != nil {
 			return "", "", errors.Trace(err)
 			return "", "", errors.Trace(err)
 		}
 		}
@@ -1489,7 +1489,7 @@ func selectHostName(
 	}
 	}
 
 
 	choice := prng.Intn(len(regexStrings))
 	choice := prng.Intn(len(regexStrings))
-	hostName, err := regen.Generate(regexStrings[choice])
+	hostName, err := regen.GenerateString(regexStrings[choice])
 	if err != nil {
 	if err != nil {
 		NoticeWarning("selectHostName: regen.Generate failed: %v", errors.Trace(err))
 		NoticeWarning("selectHostName: regen.Generate failed: %v", errors.Trace(err))
 		return values.GetHostName()
 		return values.GetHostName()

+ 0 - 28
vendor/github.com/zach-klippenstein/goregen/.gitignore

@@ -1,28 +0,0 @@
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
-*.so
-
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
-*.test
-*.prof
-
-# IntelliJ
-*.iml
-.idea/

+ 0 - 7
vendor/github.com/zach-klippenstein/goregen/.travis.yml

@@ -1,7 +0,0 @@
-language: go
-
-go:
-  - 1.5.1
-  - tip
-
-sudo: false

+ 0 - 7
vendor/github.com/zach-klippenstein/goregen/README.md

@@ -1,7 +0,0 @@
-#goregen [![GoDoc](https://godoc.org/github.com/zach-klippenstein/goregen?status.svg)](https://godoc.org/github.com/zach-klippenstein/goregen) [![Build Status](https://travis-ci.org/zach-klippenstein/goregen.svg?branch=master)](https://travis-ci.org/zach-klippenstein/goregen)
-
-A Golang library for generating random strings from regular expressions.
-
-Checkout https://goregen-demo.herokuapp.com for a live demo.
-
-See the [godoc](https://godoc.org/github.com/zach-klippenstein/goregen) for examples.

+ 0 - 9
vendor/modules.txt

@@ -125,8 +125,6 @@ github.com/google/go-cmp/cmp/internal/value
 ## explicit; go 1.12
 ## explicit; go 1.12
 github.com/google/gopacket
 github.com/google/gopacket
 github.com/google/gopacket/layers
 github.com/google/gopacket/layers
-# github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4
-## explicit
 # github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38
 # github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38
 ## explicit; go 1.14
 ## explicit; go 1.14
 github.com/google/pprof/profile
 github.com/google/pprof/profile
@@ -155,8 +153,6 @@ github.com/klauspost/compress/internal/cpuinfo
 github.com/klauspost/compress/internal/snapref
 github.com/klauspost/compress/internal/snapref
 github.com/klauspost/compress/zstd
 github.com/klauspost/compress/zstd
 github.com/klauspost/compress/zstd/internal/xxhash
 github.com/klauspost/compress/zstd/internal/xxhash
-# github.com/marten-seemann/qpack v0.3.0
-## explicit; go 1.18
 # github.com/marusama/semaphore v0.0.0-20171214154724-565ffd8e868a
 # github.com/marusama/semaphore v0.0.0-20171214154724-565ffd8e868a
 ## explicit
 ## explicit
 github.com/marusama/semaphore
 github.com/marusama/semaphore
@@ -229,8 +225,6 @@ github.com/sergeyfrolov/bsbuffer
 # github.com/sirupsen/logrus v1.8.1
 # github.com/sirupsen/logrus v1.8.1
 ## explicit; go 1.13
 ## explicit; go 1.13
 github.com/sirupsen/logrus
 github.com/sirupsen/logrus
-# github.com/smartystreets/goconvey v1.7.2
-## explicit; go 1.16
 # github.com/stretchr/testify v1.7.1
 # github.com/stretchr/testify v1.7.1
 ## explicit; go 1.13
 ## explicit; go 1.13
 github.com/stretchr/testify/assert
 github.com/stretchr/testify/assert
@@ -242,9 +236,6 @@ github.com/syndtr/gocapability/capability
 # github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78
 # github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78
 ## explicit; go 1.12
 ## explicit; go 1.12
 github.com/wader/filtertransport
 github.com/wader/filtertransport
-# github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
-## explicit
-github.com/zach-klippenstein/goregen
 # gitlab.com/yawning/obfs4.git v0.0.0-20190120164510-816cff15f425 => ./replace/obfs4.git
 # gitlab.com/yawning/obfs4.git v0.0.0-20190120164510-816cff15f425 => ./replace/obfs4.git
 ## explicit; go 1.19
 ## explicit; go 1.19
 gitlab.com/yawning/obfs4.git/common/csrand
 gitlab.com/yawning/obfs4.git/common/csrand