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