text: // Copyright 2011 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package ssh_test
  
  import (
  	"bufio"
  	"bytes"
  	"fmt"
  	"io/ioutil"
  	"log"
  	"net"
  	"net/http"
  	"os"
  	"path/filepath"
  	"strings"
  
  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/terminal"
  )
  
  func ExampleNewServerConn() {
  	// Public key authentication is done by comparing
  	// the public key of a received connection
  	// with the entries in the authorized_keys file.
  	authorizedKeysBytes, err := ioutil.ReadFile("authorized_keys")
  	if err != nil {
  		log.Fatalf("Failed to load authorized_keys, err: %v", err)
  	}
  
  	authorizedKeysMap := map[string]bool{}
  	for len(authorizedKeysBytes) > 0 {
  		pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
  		if err != nil {
  			log.Fatal(err)
  		}
  
  		authorizedKeysMap[string(pubKey.Marshal())] = true
  		authorizedKeysBytes = rest
  	}
  
  	// An SSH server is represented by a ServerConfig, which holds
  	// certificate details and handles authentication of ServerConns.
  	config := &ssh.ServerConfig{
  		// Remove to disable password auth.
  		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
  			// Should use constant-time compare (or better, salt+hash) in
  			// a production setting.
  			if c.User() == "testuser" && string(pass) == "tiger" {
  				return nil, nil
  			}
  			return nil, fmt.Errorf("password rejected for %q", c.User())
  		},
  
  		// Remove to disable public key auth.
  		PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
  			if authorizedKeysMap[string(pubKey.Marshal())] {
  				return &ssh.Permissions{
  					// Record the public key used for authentication.
  					Extensions: map[string]string{
  						"pubkey-fp": ssh.FingerprintSHA256(pubKey),
  					},
  				}, nil
  			}
  			return nil, fmt.Errorf("unknown public key for %q", c.User())
  		},
  	}
  
  	privateBytes, err := ioutil.ReadFile("id_rsa")
  	if err != nil {
  		log.Fatal("Failed to load private key: ", err)
  	}
  
  	private, err := ssh.ParsePrivateKey(privateBytes)
  	if err != nil {
  		log.Fatal("Failed to parse private key: ", err)
  	}
  
  	config.AddHostKey(private)
  
  	// Once a ServerConfig has been configured, connections can be
  	// accepted.
  	listener, err := net.Listen("tcp", "0.0.0.0:2022")
  	if err != nil {
  		log.Fatal("failed to listen for connection: ", err)
  	}
  	nConn, err := listener.Accept()
  	if err != nil {
  		log.Fatal("failed to accept incoming connection: ", err)
  	}
  
  	// Before use, a handshake must be performed on the incoming
  	// net.Conn.
  	conn, chans, reqs, err := ssh.NewServerConn(nConn, config)
  	if err != nil {
  		log.Fatal("failed to handshake: ", err)
  	}
  	log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
  
  	// The incoming Request channel must be serviced.
  	go ssh.DiscardRequests(reqs)
  
  	// Service the incoming Channel channel.
  	for newChannel := range chans {
  		// Channels have a type, depending on the application level
  		// protocol intended. In the case of a shell, the type is
  		// "session" and ServerShell may be used to present a simple
  		// terminal interface.
  		if newChannel.ChannelType() != "session" {
  			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
  			continue
  		}
  		channel, requests, err := newChannel.Accept()
  		if err != nil {
  			log.Fatalf("Could not accept channel: %v", err)
  		}
  
  		// Sessions have out-of-band requests such as "shell",
  		// "pty-req" and "env".  Here we handle only the
  		// "shell" request.
  		go func(in <-chan *ssh.Request) {
  			for req := range in {
  				req.Reply(req.Type == "shell", nil)
  			}
  		}(requests)
  
  		term := terminal.NewTerminal(channel, "> ")
  
  		go func() {
  			defer channel.Close()
  			for {
  				line, err := term.ReadLine()
  				if err != nil {
  					break
  				}
  				fmt.Println(line)
  			}
  		}()
  	}
  }
  
  func ExampleHostKeyCheck() {
  	// Every client must provide a host key check.  Here is a
  	// simple-minded parse of OpenSSH's known_hosts file
  	host := "hostname"
  	file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
  	if err != nil {
  		log.Fatal(err)
  	}
  	defer file.Close()
  
  	scanner := bufio.NewScanner(file)
  	var hostKey ssh.PublicKey
  	for scanner.Scan() {
  		fields := strings.Split(scanner.Text(), " ")
  		if len(fields) != 3 {
  			continue
  		}
  		if strings.Contains(fields[0], host) {
  			var err error
  			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
  			if err != nil {
  				log.Fatalf("error parsing %q: %v", fields[2], err)
  			}
  			break
  		}
  	}
  
  	if hostKey == nil {
  		log.Fatalf("no hostkey for %s", host)
  	}
  
  	config := ssh.ClientConfig{
  		User:            os.Getenv("USER"),
  		HostKeyCallback: ssh.FixedHostKey(hostKey),
  	}
  
  	_, err = ssh.Dial("tcp", host+":22", &config)
  	log.Println(err)
  }
  
  func ExampleDial() {
  	var hostKey ssh.PublicKey
  	// An SSH client is represented with a ClientConn.
  	//
  	// To authenticate with the remote server you must pass at least one
  	// implementation of AuthMethod via the Auth field in ClientConfig,
  	// and provide a HostKeyCallback.
  	config := &ssh.ClientConfig{
  		User: "username",
  		Auth: []ssh.AuthMethod{
  			ssh.Password("yourpassword"),
  		},
  		HostKeyCallback: ssh.FixedHostKey(hostKey),
  	}
  	client, err := ssh.Dial("tcp", "yourserver.com:22", config)
  	if err != nil {
  		log.Fatal("Failed to dial: ", err)
  	}
  
  	// Each ClientConn can support multiple interactive sessions,
  	// represented by a Session.
  	session, err := client.NewSession()
  	if err != nil {
  		log.Fatal("Failed to create session: ", err)
  	}
  	defer session.Close()
  
  	// Once a Session is created, you can execute a single command on
  	// the remote side using the Run method.
  	var b bytes.Buffer
  	session.Stdout = &b
  	if err := session.Run("/usr/bin/whoami"); err != nil {
  		log.Fatal("Failed to run: " + err.Error())
  	}
  	fmt.Println(b.String())
  }
  
  func ExamplePublicKeys() {
  	var hostKey ssh.PublicKey
  	// A public key may be used to authenticate against the remote
  	// server by using an unencrypted PEM-encoded private key file.
  	//
  	// If you have an encrypted private key, the crypto/x509 package
  	// can be used to decrypt it.
  	key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
  	if err != nil {
  		log.Fatalf("unable to read private key: %v", err)
  	}
  
  	// Create the Signer for this private key.
  	signer, err := ssh.ParsePrivateKey(key)
  	if err != nil {
  		log.Fatalf("unable to parse private key: %v", err)
  	}
  
  	config := &ssh.ClientConfig{
  		User: "user",
  		Auth: []ssh.AuthMethod{
  			// Use the PublicKeys method for remote authentication.
  			ssh.PublicKeys(signer),
  		},
  		HostKeyCallback: ssh.FixedHostKey(hostKey),
  	}
  
  	// Connect to the remote server and perform the SSH handshake.
  	client, err := ssh.Dial("tcp", "host.com:22", config)
  	if err != nil {
  		log.Fatalf("unable to connect: %v", err)
  	}
  	defer client.Close()
  }
  
  func ExampleClient_Listen() {
  	var hostKey ssh.PublicKey
  	config := &ssh.ClientConfig{
  		User: "username",
  		Auth: []ssh.AuthMethod{
  			ssh.Password("password"),
  		},
  		HostKeyCallback: ssh.FixedHostKey(hostKey),
  	}
  	// Dial your ssh server.
  	conn, err := ssh.Dial("tcp", "localhost:22", config)
  	if err != nil {
  		log.Fatal("unable to connect: ", err)
  	}
  	defer conn.Close()
  
  	// Request the remote side to open port 8080 on all interfaces.
  	l, err := conn.Listen("tcp", "0.0.0.0:8080")
  	if err != nil {
  		log.Fatal("unable to register tcp forward: ", err)
  	}
  	defer l.Close()
  
  	// Serve HTTP with your SSH server acting as a reverse proxy.
  	http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
  		fmt.Fprintf(resp, "Hello world!\n")
  	}))
  }
  
  func ExampleSession_RequestPty() {
  	var hostKey ssh.PublicKey
  	// Create client config
  	config := &ssh.ClientConfig{
  		User: "username",
  		Auth: []ssh.AuthMethod{
  			ssh.Password("password"),
  		},
  		HostKeyCallback: ssh.FixedHostKey(hostKey),
  	}
  	// Connect to ssh server
  	conn, err := ssh.Dial("tcp", "localhost:22", config)
  	if err != nil {
  		log.Fatal("unable to connect: ", err)
  	}
  	defer conn.Close()
  	// Create a session
  	session, err := conn.NewSession()
  	if err != nil {
  		log.Fatal("unable to create session: ", err)
  	}
  	defer session.Close()
  	// Set up terminal modes
  	modes := ssh.TerminalModes{
  		ssh.ECHO:          0,     // disable echoing
  		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
  		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
  	}
  	// Request pseudo terminal
  	if err := session.RequestPty("xterm", 40, 80, modes); err != nil {
  		log.Fatal("request for pseudo terminal failed: ", err)
  	}
  	// Start remote shell
  	if err := session.Shell(); err != nil {
  		log.Fatal("failed to start shell: ", err)
  	}
  }
  