Example #1
0
func (s *SSHServer) handleTCPConnection(parentTomb tomb.Tomb, conn net.Conn) error {

	// Convert to SSH connection
	sshConn, channels, requests, err := ssh.NewServerConn(conn, s.config.sshConfig)
	if err != nil {
		s.config.Logger.Warn("SSH handshake failed: %s", conn.RemoteAddr())
		return err
	}

	// Close connection on exit
	s.config.Logger.Debug("Handshake successful")
	defer sshConn.Conn.Close()

	// Discard requests
	go ssh.DiscardRequests(requests)

	// Create new tomb stone
	var t tomb.Tomb

	for {
		select {
		case ch := <-channels:
			chType := ch.ChannelType()

			// Determine if channel is acceptable (has a registered handler)
			handler, ok := s.config.Handler(chType)
			if !ok {
				s.config.Logger.Info("UnknownChannelType", "type", chType)
				ch.Reject(ssh.UnknownChannelType, chType)
				break
			}

			// Accept channel
			channel, requests, err := ch.Accept()
			if err != nil {
				s.config.Logger.Warn("Error creating channel")
				continue
			}

			t.Go(func() error {
				return handler.Handle(t, sshConn, channel, requests)
			})
		case <-parentTomb.Dying():
			t.Kill(nil)
			if err := t.Wait(); err != nil {
				s.config.Logger.Warn("ssh handler error: %s", err)
			}
			sshConn.Close()
			return sshConn.Wait()
		}
	}
	return nil
}
Example #2
0
func (s *shellHandler) Handle(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()
	s.logger.Info("WooHoo!!! Inside Handler!")

	// Create tomb for terminal goroutines
	var t tomb.Tomb

	// Sessions have out-of-band requests such as "shell",
	// "pty-req" and "env".  Here we handle only the
	// "shell" request.
	t.Go(func() error {
	OUTER:
		for {
			select {
			case <-parentTomb.Dying():
				t.Kill(nil)
				break OUTER
			case req := <-requests:
				if req == nil {
					break OUTER
				}

				ok := false
				switch req.Type {
				case "shell":
					ok = true

					if len(req.Payload) > 0 {
						// fmt.Println(string(req.Payload))

						// We don't accept any
						// commands, only the
						// default shell.
						ok = false
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true

					t.Go(func() error {
						return s.startTerminal(t, sshConn, channel)
					})
				}

				req.Reply(ok, nil)
			}
		}
		return nil
	})
	return t.Wait()
}
Example #3
0
// listen accepts new connections and handles the conversion from TCP to SSH connections.
func (s *SSHServer) listen() error {
	defer s.listener.Close()

	// Create tomb for connection goroutines
	var t tomb.Tomb
	t.Go(func() error {
	OUTER:
		for {

			// Accepts will only block for 1s
			s.listener.SetDeadline(time.Now().Add(s.config.Deadline))

			select {

			// Stop server on channel receive
			case <-s.t.Dying():
				t.Kill(nil)
				break OUTER
			default:

				// Accept new connection
				tcpConn, err := s.listener.Accept()
				if err != nil {
					if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
						s.config.Logger.Trace("Connection timeout...")
					} else {
						s.config.Logger.Warn("Connection failed", "error", err)
					}
					continue
				}

				// Handle connection
				s.config.Logger.Info("Successful TCP connection:", tcpConn.RemoteAddr().String())
				t.Go(func() error {

					// Return the error for the
					err := s.handleTCPConnection(t, tcpConn)
					if err != io.EOF {
						return err
					}
					return nil
				})
			}
		}
		return nil
	})

	return t.Wait()
}
Example #4
0
func (e *EchoHandler) Handle(t tomb.Tomb, conn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()
	e.logger.Info("echo handle called!")

	// Create tomb for terminal goroutines
	var tmb tomb.Tomb

	type msg struct {
		line     []byte
		isPrefix bool
		err      error
	}

	in := make(chan msg)
	defer close(in)
	reader := bufio.NewReader(channel)
	tmb.Go(func() error {
		tmb.Go(func() error {
			for {
				line, pre, err := reader.ReadLine()
				if err != nil {
					tmb.Kill(nil)
					return nil
				}

				select {
				case in <- msg{line, pre, err}:
				case <-t.Dying():
					tmb.Kill(nil)
					return nil
				case <-tmb.Dying():
					return nil
				}
			}
		})

		tmb.Go(func() error {
			for {
				e.logger.Info("time: ", time.Now())
				select {
				case <-tmb.Dying():
					return nil
				case <-t.Dying():
					tmb.Kill(nil)
					return nil
				case m := <-in:
					if m.err != nil {
						tmb.Kill(m.err)
						return m.err
					}

					// Send echo
					channel.Write(m.line)
				}
			}
		})
		return nil
	})

	return tmb.Wait()
}
Example #5
0
// handleTCPConnection converts the TCP connection into an ssh.ServerConn and processes all incoming channel requests
// and dispatches them to defined handlers if they exist.
func (s *SSHServer) handleTCPConnection(parentTomb tomb.Tomb, conn net.Conn) error {

	// Convert to SSH connection
	sshConn, channels, requests, err := ssh.NewServerConn(conn, s.config.sshConfig)
	if err != nil {
		s.config.Logger.Warn("SSH handshake failed:", "addr", conn.RemoteAddr().String(), "error", err)
		conn.Close()
		return err
	}

	// Close connection on exit
	s.config.Logger.Debug("Handshake successful")
	defer sshConn.Close()
	defer sshConn.Wait()

	// Discard requests
	go ssh.DiscardRequests(requests)

	// Create new tomb stone
	var t tomb.Tomb

	t.Go(func() error {
	OUTER:
		for {
			select {
			case ch := <-channels:

				// Check if chan was closed
				if ch == nil {
					t.Kill(nil)
					break OUTER
				}

				// Get channel type
				chType := ch.ChannelType()

				// Determine if channel is acceptable (has a registered handler)
				handler, ok := s.config.Handler(chType)
				if !ok {
					s.config.Logger.Info("UnknownChannelType", "type", chType)
					ch.Reject(ssh.UnknownChannelType, chType)
					t.Kill(nil)
					break OUTER
				}

				// Accept channel
				channel, requests, err := ch.Accept()
				if err != nil {
					s.config.Logger.Warn("Error creating channel")
					continue
				}

				t.Go(func() error {
					err := handler.Handle(t, sshConn, channel, requests)
					if err != nil {
						s.config.Logger.Warn("Handler raised an error", err)
					}
					s.config.Logger.Warn("Exiting channel", chType)
					t.Kill(err)
					return err
				})
			case <-parentTomb.Dying():
				t.Kill(nil)
				break OUTER
			case <-t.Dying():
				break OUTER
			}
		}
		return nil
	})

	// Wait for all goroutines to finish
	return t.Wait()
}
Example #6
0
func (e *EchoHandler) Handle(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()

	// Create tomb for terminal goroutines
	var t tomb.Tomb

	type msg struct {
		length uint32
		data   []byte
	}
	in := make(chan msg)
	defer close(in)

	// Sessions have out-of-band requests such as "shell",
	// "pty-req" and "env".  Here we handle only the
	// "shell" request.
	t.Go(func() error {
		var buffer bytes.Buffer

		// Read channel
		t.Go(func() error {

			length := make([]byte, 4)
			for {
				n, err := channel.Read(length)
				if err != nil {
					return err
				} else if n != 4 {
					return errors.New("Invalid message length")
				}

				// Decode length
				l, err := xbinary.LittleEndian.Uint32(length, 0)
				if err != nil {
					return err
				}

				// Read data
				n64, err := buffer.ReadFrom(io.LimitReader(channel, int64(l)))
				if err != nil {
					return err
				} else if n64 != int64(l) {
					return errors.New("error: reading message")
				}

				select {
				case <-parentTomb.Dying():
					return nil
				case <-t.Dying():
					return nil
				case in <- msg{l, buffer.Bytes()}:
				}
			}
		})

		length := make([]byte, 4)
	OUTER:
		for {
			select {
			case <-parentTomb.Dying():
				t.Kill(nil)
				break OUTER

			case m := <-in:
				if m.length == 0 {
					return nil
				}

				// Encode length
				_, err := xbinary.LittleEndian.PutUint32(length, 0, m.length)
				if err != nil {
					t.Kill(err)
					return nil
				}

				// Write echo response
				channel.Write(length)
				channel.Write(m.data)
			}
		}
		return nil
	})
	return t.Wait()
}