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  }