github.com/devops-filetransfer/sshego@v7.0.4+incompatible/listen.go (about)

     1  package sshego
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"net"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/glycerine/sshego/xendor/github.com/glycerine/xcryptossh"
    12  )
    13  
    14  // BasicServer configures a simple embedded sshd server
    15  // that only expects RSA key (or other) based authentication,
    16  // and doesn't expect TOTP or passphase. This makes
    17  // it suitable for using with unattended systems / to
    18  // replace a TLS server.
    19  type BasicServer struct {
    20  	cfg *SshegoConfig
    21  }
    22  
    23  // NewBasicServer in
    24  // listen.go provides net.Listen() compatibility
    25  // for running an embedded sshd. It refactors
    26  // server.go's Start() into Listen() and Accept().
    27  func NewBasicServer(cfg *SshegoConfig) *BasicServer {
    28  	cfg.NewEsshd()
    29  	return &BasicServer{cfg: cfg}
    30  }
    31  
    32  // Close releases all server port bindings.
    33  func (b *BasicServer) Close() error {
    34  	// In case we haven't yet actually started, close Done too.
    35  	// Multiple Close() calls on Halter are fine.
    36  	b.cfg.Esshd.Halt.MarkDone()
    37  	return b.cfg.Esshd.Stop()
    38  }
    39  
    40  // Address satisfies the net.Addr interface, which
    41  // BasicListener.Addr() returns.
    42  type BasicAddress struct {
    43  	addr string
    44  }
    45  
    46  // Network returns the name of the network, "sshego"
    47  func (a *BasicAddress) Network() string {
    48  	return "sshego"
    49  }
    50  
    51  // String returns the string form of the address.
    52  func (a *BasicAddress) String() string {
    53  	return a.addr
    54  }
    55  
    56  // BasicListener satifies the net.Listener interface
    57  type BasicListener struct {
    58  	bs      *BasicServer
    59  	addr    BasicAddress
    60  	esshd   *Esshd
    61  	dom     string
    62  	lsn     net.Listener
    63  	attempt uint64
    64  	halt    ssh.Halter
    65  	mut     sync.Mutex
    66  }
    67  
    68  // Addr returns the listener's network address.
    69  func (b *BasicListener) Addr() net.Addr {
    70  	return &BasicAddress{
    71  		addr: b.bs.cfg.EmbeddedSSHd.Addr,
    72  	}
    73  }
    74  
    75  // Close closes the listener.
    76  // Any blocked Accept operations will be unblocked and return errors.
    77  func (b *BasicListener) Close() error {
    78  	// in case we haven't yet actually started, close the Done
    79  	// channel too.
    80  
    81  	// global shutdown: works but not what we want!
    82  	//	b.bs.cfg.Esshd.Halt.MarkDone()
    83  	//	return b.bs.cfg.Esshd.Stop()
    84  
    85  	b.halt.MarkDone()
    86  	b.halt.RequestStop()
    87  	return nil
    88  }
    89  
    90  // Listen announces on the local network address laddr.
    91  // The syntax of laddr is "host:port", like "127.0.0.1:2222".
    92  // We listen on a TCP port.
    93  func (bs *BasicServer) Listen(laddr string) (*BasicListener, error) {
    94  	bs.cfg.EmbeddedSSHd.Addr = laddr
    95  	err := bs.cfg.EmbeddedSSHd.ParseAddr()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return bs.cfg.Esshd.Listen(bs)
   100  }
   101  
   102  // Essh add-on methods
   103  
   104  // Listen and Accept support BasicServer functionality.
   105  // Together, Listen() then Accept() replace Start().
   106  func (e *Esshd) Listen(bs *BasicServer) (*BasicListener, error) {
   107  
   108  	log.Printf("Esshd.Listen() called. %s", SourceVersion())
   109  
   110  	p("about to listen on %v", e.cfg.EmbeddedSSHd.Addr)
   111  	// Once a ServerConfig has been configured, connections can be
   112  	// accepted.
   113  	domain := "tcp"
   114  	if e.cfg.EmbeddedSSHd.UnixDomainPath != "" {
   115  		domain = "unix"
   116  	}
   117  	p("info: Essh.Listen() in listen.go: listening on "+
   118  		"domain '%s', addr: '%s'", domain, e.cfg.EmbeddedSSHd.Addr)
   119  
   120  	listener, err := net.Listen(domain, e.cfg.EmbeddedSSHd.Addr)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("failed to listen for connection on %v: %v",
   123  			e.cfg.EmbeddedSSHd.Addr, err)
   124  	}
   125  
   126  	return &BasicListener{
   127  		bs:    bs,
   128  		esshd: e,
   129  		dom:   domain,
   130  		lsn:   listener,
   131  		halt:  *ssh.NewHalter(),
   132  	}, nil
   133  }
   134  
   135  // Accept and Listen support BasicServer functionality.
   136  // Accept waits for and returns the next connection to the listener.
   137  func (b *BasicListener) Accept(ctx context.Context) (net.Conn, error) {
   138  	p("Accept for BasicListener called.")
   139  
   140  	e := b.esshd
   141  
   142  	// most of the auth state is per user, so it has
   143  	// to wait until we have a login and a
   144  	// username at hand.
   145  	a := NewAuthState(nil)
   146  
   147  	// we copy the host key here to avoid a data race later.
   148  	e.cfg.HostDb.saveMut.Lock()
   149  	a.HostKey = e.cfg.HostDb.HostSshSigner
   150  	e.cfg.HostDb.saveMut.Unlock()
   151  
   152  	// don't Close()! We may want to re-use this listener
   153  	// for another Accept().
   154  	// defer b.halt.MarkDone()
   155  
   156  	for {
   157  		// TODO: fail2ban: notice bad login IPs and if too many, block the IP.
   158  
   159  		timeoutMillisec := 1000
   160  		err := b.lsn.(*net.TCPListener).
   161  			SetDeadline(time.Now().
   162  				Add(time.Duration(timeoutMillisec) * time.Millisecond))
   163  		panicOn(err)
   164  		nConn, err := b.lsn.Accept()
   165  		p("back from Accept, err = %v", err)
   166  		if err != nil {
   167  			// simple timeout, check if stop requested
   168  			// 'accept tcp 127.0.0.1:54796: i/o timeout'
   169  			// p("simple timeout err: '%v'", err)
   170  			select {
   171  			case <-e.Halt.ReqStopChan():
   172  				p("e.Halt.ReqStop detected")
   173  				return nil, fmt.Errorf("shutting down")
   174  			case <-b.halt.ReqStopChan():
   175  				p("b.halt.ReqStop detected")
   176  				return nil, fmt.Errorf("shutting down")
   177  			default:
   178  				// no stop request, keep looping
   179  				//p("not stop request, keep looping")
   180  			}
   181  			continue
   182  		}
   183  		p("info: Essh.Accept() in listen.go: accepted new connection on "+
   184  			"domain '%s', addr: '%s'", b.dom, e.cfg.EmbeddedSSHd.Addr)
   185  
   186  		attempt := NewPerAttempt(a, e.cfg)
   187  		attempt.SetupAuthRequirements()
   188  
   189  		// need to get the direct-tcp connection back directly.
   190  		ca := &ConnectionAlert{
   191  			PortOne:  make(chan ssh.Channel),
   192  			ShutDown: b.esshd.Halt.ReqStopChan(),
   193  		}
   194  		err = attempt.PerConnection(ctx, nConn, ca)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  
   199  		select {
   200  		case <-b.halt.ReqStopChan():
   201  			return nil, fmt.Errorf("shutting down")
   202  		case <-b.esshd.Halt.ReqStopChan():
   203  			return nil, fmt.Errorf("shutting down")
   204  		case sshc := <-ca.PortOne:
   205  			return &withLocalAddr{sshc}, nil
   206  		}
   207  	} // end for
   208  }
   209  
   210  // withLocalAddr wraps an ssh.Channel to
   211  // implements the net.Conn missing methods
   212  type withLocalAddr struct {
   213  	ssh.Channel
   214  }
   215  
   216  func (w *withLocalAddr) LocalAddr() net.Addr {
   217  	panic("not implemented")
   218  	return &BasicAddress{}
   219  }
   220  func (w *withLocalAddr) RemoteAddr() net.Addr {
   221  	panic("not implemented")
   222  	return &BasicAddress{}
   223  }
   224  
   225  func (w *withLocalAddr) SetDeadline(t time.Time) error {
   226  	panic("not implemented")
   227  	return nil
   228  }
   229  func (w *withLocalAddr) SetReadDeadline(t time.Time) error {
   230  	panic("not implemented")
   231  	return nil
   232  }
   233  func (w *withLocalAddr) SetWriteDeadline(t time.Time) error {
   234  	panic("not implemented")
   235  	return nil
   236  }