Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325b735346 | ||
|
|
1eadac50a5 | ||
|
|
b2d7c26edb | ||
|
|
1c17e20020 | ||
|
|
270bf2552c | ||
|
|
1cf1811d71 | ||
|
|
a2edfb5072 | ||
|
|
ff15cd57d1 |
+7
-6
@@ -733,13 +733,14 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or,
|
// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN
|
||||||
// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue.
|
// explicitly tagged with tag into out and advances. If no element with a
|
||||||
// It reports whether the operation was successful.
|
// matching tag is present, it sets "out" to defaultValue instead. It reports
|
||||||
func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
// whether the read was successful.
|
||||||
|
func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool {
|
||||||
var present bool
|
var present bool
|
||||||
var child String
|
var child String
|
||||||
if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) {
|
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +749,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.ReadASN1Boolean(out)
|
return child.ReadASN1Boolean(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
|
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
|
||||||
|
|||||||
@@ -115,6 +115,28 @@ func TestReadASN1OptionalInteger(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultBool = false
|
||||||
|
|
||||||
|
var optionalBoolTestData = []readASN1Test{
|
||||||
|
{"empty", []byte{}, 0xa0, true, false},
|
||||||
|
{"invalid", []byte{0xa1, 0x3, 0x1, 0x2, 0x7f}, 0xa1, false, defaultBool},
|
||||||
|
{"missing", []byte{0xa1, 0x3, 0x1, 0x1, 0x7f}, 0xa0, true, defaultBool},
|
||||||
|
{"present", []byte{0xa1, 0x3, 0x1, 0x1, 0xff}, 0xa1, true, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1OptionalBoolean(t *testing.T) {
|
||||||
|
for _, test := range optionalBoolTestData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
in := String(test.in)
|
||||||
|
var out bool
|
||||||
|
ok := in.ReadOptionalASN1Boolean(&out, test.tag, defaultBool)
|
||||||
|
if ok != test.ok || ok && out != test.out.(bool) {
|
||||||
|
t.Errorf("in.ReadOptionalASN1Boolean() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadASN1IntegerSigned(t *testing.T) {
|
func TestReadASN1IntegerSigned(t *testing.T) {
|
||||||
testData64 := []struct {
|
testData64 := []struct {
|
||||||
in []byte
|
in []byte
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -26,21 +26,21 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
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/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
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/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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
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/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||||
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
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/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
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-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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ go 1.18
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.10.0 // tagx:ignore
|
golang.org/x/net v0.10.0 // tagx:ignore
|
||||||
golang.org/x/sys v0.14.0
|
golang.org/x/sys v0.15.0
|
||||||
golang.org/x/term v0.14.0
|
golang.org/x/term v0.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.14.0 // indirect
|
require golang.org/x/text v0.14.0 // indirect
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
|||||||
+19
-1
@@ -307,7 +307,10 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
}
|
}
|
||||||
var methods []string
|
var methods []string
|
||||||
var errSigAlgo error
|
var errSigAlgo error
|
||||||
for _, signer := range signers {
|
|
||||||
|
origSignersLen := len(signers)
|
||||||
|
for idx := 0; idx < len(signers); idx++ {
|
||||||
|
signer := signers[idx]
|
||||||
pub := signer.PublicKey()
|
pub := signer.PublicKey()
|
||||||
as, algo, err := pickSignatureAlgorithm(signer, extensions)
|
as, algo, err := pickSignatureAlgorithm(signer, extensions)
|
||||||
if err != nil && errSigAlgo == nil {
|
if err != nil && errSigAlgo == nil {
|
||||||
@@ -321,6 +324,21 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return authFailure, nil, err
|
return authFailure, nil, err
|
||||||
}
|
}
|
||||||
|
// OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
|
||||||
|
// in the "server-sig-algs" extension but doesn't support these
|
||||||
|
// algorithms for certificate authentication, so if the server rejects
|
||||||
|
// the key try to use the obtained algorithm as if "server-sig-algs" had
|
||||||
|
// not been implemented if supported from the algorithm signer.
|
||||||
|
if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
|
||||||
|
if contains(as.Algorithms(), KeyAlgoRSA) {
|
||||||
|
// We retry using the compat algorithm after all signers have
|
||||||
|
// been tried normally.
|
||||||
|
signers = append(signers, &multiAlgorithmSigner{
|
||||||
|
AlgorithmSigner: as,
|
||||||
|
supportedAlgorithms: []string{KeyAlgoRSA},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,14 @@ func isRSA(algo string) bool {
|
|||||||
return contains(algos, underlyingAlgo(algo))
|
return contains(algos, underlyingAlgo(algo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRSACert(algo string) bool {
|
||||||
|
_, ok := certKeyAlgoNames[algo]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isRSA(algo)
|
||||||
|
}
|
||||||
|
|
||||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||||
// authentication algorithms. Note that this doesn't include certificate types
|
// authentication algorithms. Note that this doesn't include certificate types
|
||||||
// since those use the underlying algorithm. This list is sent to the client if
|
// since those use the underlying algorithm. This list is sent to the client if
|
||||||
|
|||||||
+16
-2
@@ -16,6 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
@@ -98,8 +99,15 @@ func ExampleNewServerConn() {
|
|||||||
}
|
}
|
||||||
log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
|
log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
// The incoming Request channel must be serviced.
|
// The incoming Request channel must be serviced.
|
||||||
go ssh.DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
ssh.DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
// Service the incoming Channel channel.
|
// Service the incoming Channel channel.
|
||||||
for newChannel := range chans {
|
for newChannel := range chans {
|
||||||
@@ -119,16 +127,22 @@ func ExampleNewServerConn() {
|
|||||||
// Sessions have out-of-band requests such as "shell",
|
// Sessions have out-of-band requests such as "shell",
|
||||||
// "pty-req" and "env". Here we handle only the
|
// "pty-req" and "env". Here we handle only the
|
||||||
// "shell" request.
|
// "shell" request.
|
||||||
|
wg.Add(1)
|
||||||
go func(in <-chan *ssh.Request) {
|
go func(in <-chan *ssh.Request) {
|
||||||
for req := range in {
|
for req := range in {
|
||||||
req.Reply(req.Type == "shell", nil)
|
req.Reply(req.Type == "shell", nil)
|
||||||
}
|
}
|
||||||
|
wg.Done()
|
||||||
}(requests)
|
}(requests)
|
||||||
|
|
||||||
term := terminal.NewTerminal(channel, "> ")
|
term := terminal.NewTerminal(channel, "> ")
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer channel.Close()
|
defer func() {
|
||||||
|
channel.Close()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for {
|
for {
|
||||||
line, err := term.ReadLine()
|
line, err := term.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+49
-42
@@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func muxPair() (*mux, *mux) {
|
func muxPair() (*mux, *mux) {
|
||||||
@@ -112,7 +111,11 @@ func TestMuxReadWrite(t *testing.T) {
|
|||||||
|
|
||||||
magic := "hello world"
|
magic := "hello world"
|
||||||
magicExt := "hello stderr"
|
magicExt := "hello stderr"
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
_, err := s.Write([]byte(magic))
|
_, err := s.Write([]byte(magic))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Write: %v", err)
|
t.Errorf("Write: %v", err)
|
||||||
@@ -152,13 +155,15 @@ func TestMuxChannelOverflow(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
writer.Write(make([]byte, 1))
|
writer.Write(make([]byte, 1))
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
|
|
||||||
@@ -175,7 +180,6 @@ func TestMuxChannelOverflow(t *testing.T) {
|
|||||||
if _, err := reader.SendRequest("hello", true, nil); err == nil {
|
if _, err := reader.SendRequest("hello", true, nil); err == nil {
|
||||||
t.Errorf("SendRequest succeeded.")
|
t.Errorf("SendRequest succeeded.")
|
||||||
}
|
}
|
||||||
<-wDone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
||||||
@@ -184,20 +188,21 @@ func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
||||||
t.Errorf("got %v, want EOF for unblock write", err)
|
t.Errorf("got %v, want EOF for unblock write", err)
|
||||||
}
|
}
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
reader.Close()
|
reader.Close()
|
||||||
<-wDone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
||||||
@@ -206,20 +211,21 @@ func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
||||||
t.Errorf("got %v, want EOF for unblock write", err)
|
t.Errorf("got %v, want EOF for unblock write", err)
|
||||||
}
|
}
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
mux.Close()
|
mux.Close()
|
||||||
<-wDone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxReject(t *testing.T) {
|
func TestMuxReject(t *testing.T) {
|
||||||
@@ -227,7 +233,12 @@ func TestMuxReject(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
ch, ok := <-server.incomingChannels
|
ch, ok := <-server.incomingChannels
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("cannot accept channel")
|
t.Error("cannot accept channel")
|
||||||
@@ -267,6 +278,7 @@ func TestMuxChannelRequest(t *testing.T) {
|
|||||||
|
|
||||||
var received int
|
var received int
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
for r := range server.incomingRequests {
|
for r := range server.incomingRequests {
|
||||||
@@ -295,7 +307,6 @@ func TestMuxChannelRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
t.Errorf("SendRequest(no): %v", ok)
|
t.Errorf("SendRequest(no): %v", ok)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Close()
|
client.Close()
|
||||||
@@ -389,13 +400,8 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
|
|
||||||
// Wait for the server to send the keepalive message and receive back a
|
// Wait for the server to send the keepalive message and receive back a
|
||||||
// response.
|
// response.
|
||||||
select {
|
if err := <-kDone; err != nil {
|
||||||
case err := <-kDone:
|
t.Fatal(err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never received ack")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm client hasn't closed.
|
// Confirm client hasn't closed.
|
||||||
@@ -403,13 +409,9 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
t.Fatalf("failed to send keepalive: %v", err)
|
t.Fatalf("failed to send keepalive: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
// Wait for the server to shut down.
|
||||||
case err := <-kDone:
|
if err := <-kDone; err != nil {
|
||||||
if err != nil {
|
t.Fatal(err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never shut down")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,11 +527,7 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
defer ch.Close()
|
defer ch.Close()
|
||||||
|
|
||||||
// Wait for the server to close the channel and send the keepalive.
|
// Wait for the server to close the channel and send the keepalive.
|
||||||
select {
|
<-kDone
|
||||||
case <-kDone:
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never received ack")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the channel closed.
|
// Make sure the channel closed.
|
||||||
if _, ok := <-ch.incomingRequests; ok {
|
if _, ok := <-ch.incomingRequests; ok {
|
||||||
@@ -541,22 +539,29 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
t.Fatalf("failed to send keepalive: %v", err)
|
t.Fatalf("failed to send keepalive: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
// Wait for the server to shut down.
|
||||||
case <-kDone:
|
<-kDone
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never shut down")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxGlobalRequest(t *testing.T) {
|
func TestMuxGlobalRequest(t *testing.T) {
|
||||||
|
var sawPeek bool
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer func() {
|
||||||
|
wg.Wait()
|
||||||
|
if !sawPeek {
|
||||||
|
t.Errorf("never saw 'peek' request")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
clientMux, serverMux := muxPair()
|
clientMux, serverMux := muxPair()
|
||||||
defer serverMux.Close()
|
defer serverMux.Close()
|
||||||
defer clientMux.Close()
|
defer clientMux.Close()
|
||||||
|
|
||||||
var seen bool
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
for r := range serverMux.incomingRequests {
|
for r := range serverMux.incomingRequests {
|
||||||
seen = seen || r.Type == "peek"
|
sawPeek = sawPeek || r.Type == "peek"
|
||||||
if r.WantReply {
|
if r.WantReply {
|
||||||
err := r.Reply(r.Type == "yes",
|
err := r.Reply(r.Type == "yes",
|
||||||
append([]byte(r.Type), r.Payload...))
|
append([]byte(r.Type), r.Payload...))
|
||||||
@@ -586,10 +591,6 @@ func TestMuxGlobalRequest(t *testing.T) {
|
|||||||
t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v",
|
t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v",
|
||||||
ok, data, err)
|
ok, data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !seen {
|
|
||||||
t.Errorf("never saw 'peek' request")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxGlobalRequestUnblock(t *testing.T) {
|
func TestMuxGlobalRequestUnblock(t *testing.T) {
|
||||||
@@ -739,7 +740,13 @@ func TestMuxMaxPacketSize(t *testing.T) {
|
|||||||
t.Errorf("could not send packet")
|
t.Errorf("could not send packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
go a.SendRequest("hello", false, nil)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
a.SendRequest("hello", false, nil)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
_, ok := <-b.incomingRequests
|
_, ok := <-b.incomingRequests
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
+3
-2
@@ -337,7 +337,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
|
|||||||
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
|
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection,
|
func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection,
|
||||||
sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
|
sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
|
||||||
gssAPIServer := gssapiConfig.Server
|
gssAPIServer := gssapiConfig.Server
|
||||||
defer gssAPIServer.DeleteSecContext()
|
defer gssAPIServer.DeleteSecContext()
|
||||||
@@ -347,7 +347,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
|
|||||||
outToken []byte
|
outToken []byte
|
||||||
needContinue bool
|
needContinue bool
|
||||||
)
|
)
|
||||||
outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken)
|
outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil, nil
|
return err, nil, nil
|
||||||
}
|
}
|
||||||
@@ -369,6 +369,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
|
|||||||
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
|
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
token = userAuthGSSAPITokenReq.Token
|
||||||
}
|
}
|
||||||
packet, err := s.transport.readPacket()
|
packet, err := s.transport.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+59
-12
@@ -13,6 +13,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
@@ -27,8 +28,14 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Fatalf("netPipe: %v", err)
|
t.Fatalf("netPipe: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer c1.Close()
|
defer func() {
|
||||||
|
c1.Close()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
conf := ServerConfig{
|
conf := ServerConfig{
|
||||||
NoClientAuth: true,
|
NoClientAuth: true,
|
||||||
}
|
}
|
||||||
@@ -39,7 +46,11 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Errorf("Unable to handshake: %v", err)
|
t.Errorf("Unable to handshake: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
for newCh := range chans {
|
for newCh := range chans {
|
||||||
if newCh.ChannelType() != "session" {
|
if newCh.ChannelType() != "session" {
|
||||||
@@ -52,8 +63,10 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Errorf("Accept: %v", err)
|
t.Errorf("Accept: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
handler(ch, inReqs, t)
|
handler(ch, inReqs, t)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if err := conn.Wait(); err != io.EOF {
|
if err := conn.Wait(); err != io.EOF {
|
||||||
@@ -338,8 +351,13 @@ func TestServerWindow(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
result := make(chan []byte)
|
|
||||||
|
|
||||||
|
serverStdin, err := session.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("StdinPipe failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(chan []byte)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(result)
|
defer close(result)
|
||||||
echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
|
echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
|
||||||
@@ -355,10 +373,6 @@ func TestServerWindow(t *testing.T) {
|
|||||||
result <- echoedBuf.Bytes()
|
result <- echoedBuf.Bytes()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
serverStdin, err := session.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("StdinPipe failed: %v", err)
|
|
||||||
}
|
|
||||||
written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
|
written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to copy origBuf to serverStdin: %v", err)
|
t.Errorf("failed to copy origBuf to serverStdin: %v", err)
|
||||||
@@ -648,29 +662,44 @@ func TestSessionID(t *testing.T) {
|
|||||||
User: "user",
|
User: "user",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
|
||||||
srvErrCh := make(chan error, 1)
|
srvErrCh := make(chan error, 1)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
||||||
srvErrCh <- err
|
srvErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serverID <- conn.SessionID()
|
serverID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cliErrCh := make(chan error, 1)
|
cliErrCh := make(chan error, 1)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
||||||
cliErrCh <- err
|
cliErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clientID <- conn.SessionID()
|
clientID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
@@ -738,6 +767,8 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
serverConf.AddHostKey(testSigners["rsa"])
|
serverConf.AddHostKey(testSigners["rsa"])
|
||||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
connect := func(clientConf *ClientConfig, want string) {
|
connect := func(clientConf *ClientConfig, want string) {
|
||||||
var alg string
|
var alg string
|
||||||
clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
|
clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
|
||||||
@@ -751,7 +782,11 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
defer c1.Close()
|
defer c1.Close()
|
||||||
defer c2.Close()
|
defer c2.Close()
|
||||||
|
|
||||||
go NewServerConn(c1, serverConf)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
NewServerConn(c1, serverConf)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClientConn: %v", err)
|
t.Fatalf("NewClientConn: %v", err)
|
||||||
@@ -785,7 +820,11 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
defer c1.Close()
|
defer c1.Close()
|
||||||
defer c2.Close()
|
defer c2.Close()
|
||||||
|
|
||||||
go NewServerConn(c1, serverConf)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
NewServerConn(c1, serverConf)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
|
clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
|
||||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -818,14 +857,22 @@ func TestServerClientAuthCallback(t *testing.T) {
|
|||||||
User: someUsername,
|
User: someUsername,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
_, chans, reqs, err := NewServerConn(c1, serverConf)
|
_, chans, reqs, err := NewServerConn(c1, serverConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("server handshake: %v", err)
|
t.Errorf("server handshake: %v", err)
|
||||||
userCh <- "error"
|
userCh <- "error"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -332,6 +333,40 @@ func (l *tcpListener) Addr() net.Addr {
|
|||||||
return l.laddr
|
return l.laddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DialContext initiates a connection to the addr from the remote host.
|
||||||
|
//
|
||||||
|
// The provided Context must be non-nil. If the context expires before the
|
||||||
|
// connection is complete, an error is returned. Once successfully connected,
|
||||||
|
// any expiration of the context will not affect the connection.
|
||||||
|
//
|
||||||
|
// See func Dial for additional information.
|
||||||
|
func (c *Client) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
type connErr struct {
|
||||||
|
conn net.Conn
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
ch := make(chan connErr)
|
||||||
|
go func() {
|
||||||
|
conn, err := c.Dial(n, addr)
|
||||||
|
select {
|
||||||
|
case ch <- connErr{conn, err}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
if conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case res := <-ch:
|
||||||
|
return res.conn, res.err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dial initiates a connection to the addr from the remote host.
|
// Dial initiates a connection to the addr from the remote host.
|
||||||
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
||||||
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAutoPortListenBroken(t *testing.T) {
|
func TestAutoPortListenBroken(t *testing.T) {
|
||||||
@@ -18,3 +21,33 @@ func TestAutoPortListenBroken(t *testing.T) {
|
|||||||
t.Errorf("version %q marked as broken", works)
|
t.Errorf("version %q marked as broken", works)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClientImplementsDialContext(t *testing.T) {
|
||||||
|
type ContextDialer interface {
|
||||||
|
DialContext(context.Context, string, string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
// Belt and suspenders assertion, since package net does not
|
||||||
|
// declare a ContextDialer type.
|
||||||
|
var _ ContextDialer = &net.Dialer{}
|
||||||
|
var _ ContextDialer = &Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDialContextWithCancel(t *testing.T) {
|
||||||
|
c := &Client{}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
_, err := c.DialContext(ctx, "tcp", "localhost:1000")
|
||||||
|
if err != context.Canceled {
|
||||||
|
t.Errorf("DialContext: got nil error, expected %v", context.Canceled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDialContextWithDeadline(t *testing.T) {
|
||||||
|
c := &Client{}
|
||||||
|
ctx, cancel := context.WithDeadline(context.Background(), time.Now())
|
||||||
|
defer cancel()
|
||||||
|
_, err := c.DialContext(ctx, "tcp", "localhost:1000")
|
||||||
|
if err != context.DeadlineExceeded {
|
||||||
|
t.Errorf("DialContext: got nil error, expected %v", context.DeadlineExceeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ package test
|
|||||||
// direct-tcpip and direct-streamlocal functional tests
|
// direct-tcpip and direct-streamlocal functional tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -46,7 +47,11 @@ func testDial(t *testing.T, n, listenAddr string, x dialTester) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn, err := sshConn.Dial(n, l.Addr().String())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
conn, err := sshConn.DialContext(ctx, n, l.Addr().String())
|
||||||
|
// Canceling the context after dial should have no effect
|
||||||
|
// on the opened connection.
|
||||||
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Dial: %v", err)
|
t.Fatalf("Dial: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/internal/testenv"
|
"golang.org/x/crypto/internal/testenv"
|
||||||
@@ -34,6 +35,9 @@ func sshClient(t *testing.T) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSSHCLIAuth(t *testing.T) {
|
func TestSSHCLIAuth(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skipf("always fails on Windows, see #64403")
|
||||||
|
}
|
||||||
sshCLI := sshClient(t)
|
sshCLI := sshClient(t)
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
keyPrivPath := filepath.Join(dir, "rsa")
|
keyPrivPath := filepath.Join(dir, "rsa")
|
||||||
|
|||||||
Reference in New Issue
Block a user