proxy ver1
This commit is contained in:
		
						commit
						c15c1c0421
					
				
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
module ssh-proxy
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.4 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										8
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
							
								
								
									
										55
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"ssh-proxy/pkg/logger"
 | 
			
		||||
	"ssh-proxy/pkg/proxy"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	fmt.Println("vim-go")
 | 
			
		||||
 | 
			
		||||
	laddr, err := net.ResolveTCPAddr("tcp", ":9999")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	raddr, err := net.ResolveTCPAddr("tcp", "localhost:22")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listener, err := net.ListenTCP("tcp", laddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var connid uint64 = 0
 | 
			
		||||
	for {
 | 
			
		||||
		conn, err := listener.AcceptTCP()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Println(err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		connid++
 | 
			
		||||
		var p *proxy.Proxy
 | 
			
		||||
 | 
			
		||||
		p = proxy.New(conn, laddr, raddr)
 | 
			
		||||
		p.Nagles = false
 | 
			
		||||
		p.OutputHex = true
 | 
			
		||||
		p.Log = logger.ColorLogger{
 | 
			
		||||
			VeryVerbose: true,
 | 
			
		||||
			Verbose:     true,
 | 
			
		||||
			Prefix:      fmt.Sprintf("Connection: #%03d ", connid),
 | 
			
		||||
			Color:       true,
 | 
			
		||||
		}
 | 
			
		||||
		go func() {
 | 
			
		||||
			err := p.Start()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Println("conn err ::: ", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								pkg/common/common.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								pkg/common/common.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"ssh-proxy/pkg/logger"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func CheckRemoteSSH(host string, port int, timeout int) bool {
 | 
			
		||||
	log := logger.ColorLogger{}
 | 
			
		||||
	connTimeout := time.Duration(timeout) * time.Second
 | 
			
		||||
	if connTimeout <= 0 {
 | 
			
		||||
		connTimeout = time.Second * 5
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(port)), connTimeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Trace("Check Remote Host SSH Fail: Host(%s:%d) %s", host, port, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								pkg/common/common_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								pkg/common/common_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestCheckRemoteSSH(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		host    string
 | 
			
		||||
		port    int
 | 
			
		||||
		timeout int
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
		want bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "test check localhost ssh port",
 | 
			
		||||
			args: args{
 | 
			
		||||
				host:    "localhost",
 | 
			
		||||
				port:    22,
 | 
			
		||||
				timeout: 5,
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := CheckRemoteSSH(tt.args.host, tt.args.port, tt.args.timeout); got != tt.want {
 | 
			
		||||
				t.Errorf("CheckRemoteSSH() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								pkg/logger/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								pkg/logger/logger.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	Trace(f string, args ...interface{})
 | 
			
		||||
	Debug(f string, args ...interface{})
 | 
			
		||||
	Info(f string, args ...interface{})
 | 
			
		||||
	Warn(f string, args ...interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NullLogger struct{}
 | 
			
		||||
 | 
			
		||||
func (n NullLogger) Trace(f string, args ...interface{}) {}
 | 
			
		||||
 | 
			
		||||
func (n NullLogger) Debug(f string, args ...interface{}) {}
 | 
			
		||||
 | 
			
		||||
func (n NullLogger) Info(f string, args ...interface{}) {}
 | 
			
		||||
 | 
			
		||||
func (n NullLogger) Warn(f string, args ...interface{}) {}
 | 
			
		||||
 | 
			
		||||
type ColorLogger struct {
 | 
			
		||||
	VeryVerbose bool
 | 
			
		||||
	Verbose     bool
 | 
			
		||||
	Prefix      string
 | 
			
		||||
	Color       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ColorLogger) Trace(f string, args ...interface{}) {
 | 
			
		||||
	if !c.VeryVerbose {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.output("blue", f, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ColorLogger) Debug(f string, args ...interface{}) {
 | 
			
		||||
	if !c.Verbose {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.output("green", f, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ColorLogger) Info(f string, args ...interface{}) {
 | 
			
		||||
	c.output("green", f, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ColorLogger) Warn(f string, args ...interface{}) {
 | 
			
		||||
	c.output("red", f, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ColorLogger) output(color, f string, args ...interface{}) {
 | 
			
		||||
	if c.Color && color != "" {
 | 
			
		||||
		f = ansi.Color(f, color)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf(fmt.Sprintf("%s%s\n", c.Prefix, f), args...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								pkg/proxy/proxy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								pkg/proxy/proxy.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
			
		||||
package proxy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"ssh-proxy/pkg/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Proxy struct {
 | 
			
		||||
	sendBytes     uint64
 | 
			
		||||
	receivedBytes uint64
 | 
			
		||||
	laddr, raddr  *net.TCPAddr
 | 
			
		||||
	lconn, rconn  io.ReadWriteCloser
 | 
			
		||||
	erred         bool
 | 
			
		||||
	errsig        chan error
 | 
			
		||||
	tlsUnwrapp    bool
 | 
			
		||||
	tlsAddress    string
 | 
			
		||||
 | 
			
		||||
	Matcher  func([]byte)
 | 
			
		||||
	Replacer func([]byte) []byte
 | 
			
		||||
 | 
			
		||||
	// setting
 | 
			
		||||
	Nagles    bool
 | 
			
		||||
	Log       logger.Logger
 | 
			
		||||
	OutputHex bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(lconn *net.TCPConn, laddr, raddr *net.TCPAddr) *Proxy {
 | 
			
		||||
	return &Proxy{
 | 
			
		||||
		lconn:  lconn,
 | 
			
		||||
		laddr:  laddr,
 | 
			
		||||
		raddr:  raddr,
 | 
			
		||||
		erred:  false,
 | 
			
		||||
		errsig: make(chan error),
 | 
			
		||||
		Log:    logger.NullLogger{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewTLSUnwarpped(lconn *net.TCPConn, laddr, raddr *net.TCPAddr, addr string) *Proxy {
 | 
			
		||||
	p := New(lconn, laddr, raddr)
 | 
			
		||||
	p.tlsUnwrapp = true
 | 
			
		||||
	p.tlsAddress = addr
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type setNoDelayer interface {
 | 
			
		||||
	SetNoDelay(bool) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Proxy) Start() (err error) {
 | 
			
		||||
	defer p.lconn.Close()
 | 
			
		||||
 | 
			
		||||
	if p.tlsUnwrapp {
 | 
			
		||||
		p.rconn, err = tls.Dial("tcp", p.tlsAddress, nil)
 | 
			
		||||
	} else {
 | 
			
		||||
		p.rconn, err = net.DialTCP("tcp", nil, p.raddr)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.Log.Warn("Remote connection failed: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer p.rconn.Close()
 | 
			
		||||
 | 
			
		||||
	// nagles
 | 
			
		||||
	if p.Nagles {
 | 
			
		||||
		if conn, ok := p.lconn.(setNoDelayer); ok {
 | 
			
		||||
			conn.SetNoDelay(true)
 | 
			
		||||
		}
 | 
			
		||||
		if conn, ok := p.rconn.(setNoDelayer); ok {
 | 
			
		||||
			conn.SetNoDelay(true)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//display both ends
 | 
			
		||||
	p.Log.Info("Opened %s >>>> %s", p.laddr.String(), p.raddr.String())
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
 | 
			
		||||
	//bidirectional copy
 | 
			
		||||
	go p.pipe(ctx, p.lconn, p.rconn)
 | 
			
		||||
	go p.pipe(ctx, p.rconn, p.lconn)
 | 
			
		||||
 | 
			
		||||
	// wait for close
 | 
			
		||||
	err = <-p.errsig
 | 
			
		||||
	p.Log.Info("Closed (%d bytes send, %d bytes received)", p.sendBytes, p.receivedBytes)
 | 
			
		||||
	cancel()
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Proxy) err(s string, err error) {
 | 
			
		||||
	if p.erred {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != io.EOF {
 | 
			
		||||
		p.Log.Warn(s, err)
 | 
			
		||||
	}
 | 
			
		||||
	p.errsig <- err
 | 
			
		||||
	p.erred = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Proxy) pipe(ctx context.Context, src, dst io.ReadWriter) {
 | 
			
		||||
	islocal := src == p.lconn
 | 
			
		||||
 | 
			
		||||
	var dataDirection string
 | 
			
		||||
	if islocal {
 | 
			
		||||
		dataDirection = ">>>> %d bytes sent%s"
 | 
			
		||||
	} else {
 | 
			
		||||
		dataDirection = "<<<< %d bytes received%s"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var byteFormat string
 | 
			
		||||
	if p.OutputHex {
 | 
			
		||||
		byteFormat = "%x"
 | 
			
		||||
	} else {
 | 
			
		||||
		byteFormat = "%s"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// directional copy (64k buffer)
 | 
			
		||||
	buff := make([]byte, 0xffff)
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ctx.Done():
 | 
			
		||||
			return
 | 
			
		||||
		default:
 | 
			
		||||
			n, err := src.Read(buff)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				p.err("Read failed '%s'\n", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			b := buff[:n]
 | 
			
		||||
 | 
			
		||||
			// execute match
 | 
			
		||||
			if p.Matcher != nil {
 | 
			
		||||
				p.Matcher(b)
 | 
			
		||||
			}
 | 
			
		||||
			// execute replace
 | 
			
		||||
			if p.Replacer != nil {
 | 
			
		||||
				b = p.Replacer(b)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// show output
 | 
			
		||||
			p.Log.Debug(dataDirection, n, "")
 | 
			
		||||
			p.Log.Trace(byteFormat, b)
 | 
			
		||||
 | 
			
		||||
			// write to result
 | 
			
		||||
			n, err = dst.Write(b)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				p.err("Write failed '%s'\n", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if islocal {
 | 
			
		||||
				p.sendBytes += uint64(n)
 | 
			
		||||
			} else {
 | 
			
		||||
				p.receivedBytes += uint64(n)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user