-
Notifications
You must be signed in to change notification settings - Fork 19
/
redial_test.go
174 lines (139 loc) · 4.99 KB
/
redial_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package sshego
import (
"context"
"fmt"
"net"
"runtime/debug"
// "io/ioutil"
// "log"
"strings"
"testing"
"time"
cv "github.com/glycerine/goconvey/convey"
ssh "github.com/glycerine/sshego/xendor/github.com/glycerine/xcryptossh"
)
func init() {
// see all goroutines on panic for proper debugging of tests.
debug.SetTraceback("all")
}
// this can be flakey in batch run of all tests...
func Test050RedialCleanlyIsPossible(t *testing.T) {
cv.Convey("Unless cfg.SkipKeepAlive, if our client has done sub := clientSshegoCfg.ClientReconnectNeededTower.Subscribe() and is later disconnected from the ssh server, then: we receive a notification on sub that reconnect is needed. We should be able to redial cleanly and create a new direct tcp channel to resume communication with a downstream server.", t, func() {
// since can be red under go test -v but green
// when run standalone, try sleeping for a few seconds to
// let ports reset?
time.Sleep(time.Second * 2)
// start a simple TCP server that is the target of the forward through the sshd,
// so we can confirm the client has made the connection.
// generate a random payload for the client to send to the server.
payloadByteCount := 50
confirmationPayload := RandomString(payloadByteCount)
confirmationReply := RandomString(payloadByteCount)
mgr := ssh.NewHalter()
tcpSrvLsn, tcpSrvPort := GetAvailPort()
var nc net.Conn
StartBackgroundTestTcpServer(
mgr,
payloadByteCount,
confirmationPayload,
confirmationReply,
tcpSrvLsn,
&nc)
s := MakeTestSshClientAndServer(true)
defer TempDirCleanup(s.SrvCfg.Origdir, s.SrvCfg.Tempdir)
dest := fmt.Sprintf("127.0.0.1:%v", tcpSrvPort)
// below over SSH should be equivalent of the following
// non-encrypted ping/pong.
dc := DialConfig{
ClientKnownHostsPath: s.CliCfg.ClientKnownHostsPath,
Mylogin: s.Mylogin,
RsaPath: s.RsaPath,
TotpUrl: s.Totp,
Pw: s.Pw,
Sshdhost: s.SrvCfg.EmbeddedSSHd.Host,
Sshdport: s.SrvCfg.EmbeddedSSHd.Port,
DownstreamHostPort: dest,
TofuAddIfNotKnown: true,
// essential for this test to work!
KeepAliveEvery: time.Second,
}
tries := 0
needReconnectCh := make(chan *UHP, 1)
var channelToTcpServer net.Conn
var clientSshegoCfg *SshegoConfig
var err error
ctx := context.Background()
for ; tries < 3; tries++ {
// first time we add the server key
channelToTcpServer, _, _, err = dc.Dial(ctx, nil, false)
fmt.Printf("after dc.Dial() in cli_test.go: err = '%v'", err)
errs := err.Error()
case1 := strings.Contains(errs, "Re-run without -new")
case2 := strings.Contains(errs, "getsockopt: connection refused")
ok := case1 || case2
cv.So(ok, cv.ShouldBeTrue)
if case1 {
break
}
}
if tries == 3 {
panic("could not get 'Re-run without -new' after 3 tries")
}
// second time we connect based on that server key
dc.TofuAddIfNotKnown = false
channelToTcpServer, _, clientSshegoCfg, err = dc.Dial(ctx, nil, false)
cv.So(err, cv.ShouldBeNil)
clientSshegoCfg.ClientReconnectNeededTower.Subscribe(needReconnectCh)
<-mgr.ReadyChan()
VerifyClientServerExchangeAcrossSshd(channelToTcpServer, confirmationPayload, confirmationReply, payloadByteCount)
mgr.RequestStop()
<-mgr.DoneChan()
nc.Close()
nc = nil
channelToTcpServer.Close()
pp("starting on 2nd confirmation")
s.SrvCfg.Halt.RequestStop()
<-s.SrvCfg.Halt.DoneChan()
// after killing remote sshd
var uhp *UHP
select {
case uhp = <-needReconnectCh:
pp("good, 050 got needReconnectCh to '%#v'", uhp)
case <-time.After(5 * time.Second):
panic("never received <-needReconnectCh: timeout after 5 seconds")
}
cv.So(uhp.User, cv.ShouldEqual, dc.Mylogin)
destHostPort := fmt.Sprintf("%v:%v", dc.Sshdhost, dc.Sshdport)
cv.So(uhp.HostPort, cv.ShouldEqual, destHostPort)
// so restart the sshd server
pp("waiting for destHostPort='%v' to be availble", destHostPort)
panicOn(s.SrvCfg.Esshd.Stop())
s.SrvCfg.Reset()
s.SrvCfg.NewEsshd()
s.SrvCfg.Esshd.Start(ctx)
serverDone2 := ssh.NewHalter()
confirmationPayload2 := RandomString(payloadByteCount)
confirmationReply2 := RandomString(payloadByteCount)
StartBackgroundTestTcpServer(
serverDone2,
payloadByteCount,
confirmationPayload2,
confirmationReply2,
tcpSrvLsn, &nc)
// can this Dial be made automatic re-Dial?
// the net.Conn and the sshClient need to
// be changed.
channelToTcpServer, _, _, err = dc.Dial(ctx, nil, false)
cv.So(err, cv.ShouldBeNil)
VerifyClientServerExchangeAcrossSshd(channelToTcpServer, confirmationPayload2, confirmationReply2, payloadByteCount)
// tcp-server should have exited because it got the expected
// message and replied with the agreed upon reply and then exited.
serverDone2.RequestStop()
<-serverDone2.DoneChan()
nc.Close()
// done with testing, cleanup
s.SrvCfg.Esshd.Stop()
<-s.SrvCfg.Esshd.Halt.DoneChan()
cv.So(true, cv.ShouldEqual, true) // we should get here.
})
}