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…
Reference in New Issue
Block a user