105 lines
2.3 KiB
Go
105 lines
2.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/tls"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type SclientOpts struct {
|
||
|
RemoteAddress string
|
||
|
RemotePort int
|
||
|
LocalAddress string
|
||
|
LocalPort int
|
||
|
InsecureSkipVerify bool
|
||
|
}
|
||
|
|
||
|
type Sclient struct{}
|
||
|
|
||
|
func pipe(r net.Conn, w net.Conn, t string) {
|
||
|
buffer := make([]byte, 2048)
|
||
|
for {
|
||
|
done := false
|
||
|
// NOTE: count may be > 0 even if there's an err
|
||
|
count, err := r.Read(buffer)
|
||
|
//fmt.Fprintf(os.Stdout, "[debug] (%s) reading\n", t)
|
||
|
if nil != err {
|
||
|
//fmt.Fprintf(os.Stdout, "[debug] (%s:%d) error reading %s\n", t, count, err)
|
||
|
if io.EOF != err {
|
||
|
fmt.Fprintf(os.Stderr, "[read error] (%s:%s) %s\n", t, count, err)
|
||
|
}
|
||
|
r.Close()
|
||
|
//w.Close()
|
||
|
done = true
|
||
|
}
|
||
|
if 0 == count {
|
||
|
break
|
||
|
}
|
||
|
_, err = w.Write(buffer[:count])
|
||
|
if nil != err {
|
||
|
//fmt.Fprintf(os.Stdout, "[debug] %s error writing\n", t)
|
||
|
if io.EOF != err {
|
||
|
fmt.Fprintf(os.Stderr, "[write error] (%s) %s\n", t, err)
|
||
|
}
|
||
|
// TODO handle error closing?
|
||
|
r.Close()
|
||
|
//w.Close()
|
||
|
done = true
|
||
|
}
|
||
|
if done {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func handleConnection(remote string, conn net.Conn, opts *SclientOpts) {
|
||
|
sclient, err := tls.Dial("tcp", remote,
|
||
|
&tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify})
|
||
|
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "[error] (remote) %s\n", err)
|
||
|
conn.Close()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fmt.Fprintf(os.Stdout, "[connect] %s => %s:%d\n",
|
||
|
strings.Replace(conn.RemoteAddr().String(), "[::1]:", "localhost:", 1), opts.RemoteAddress, opts.RemotePort)
|
||
|
|
||
|
go pipe(conn, sclient, "local")
|
||
|
pipe(sclient, conn, "remote")
|
||
|
}
|
||
|
|
||
|
func (*Sclient) DialAndListen(opts *SclientOpts) error {
|
||
|
remote := opts.RemoteAddress + ":" + strconv.Itoa(opts.RemotePort)
|
||
|
conn, err := tls.Dial("tcp", remote,
|
||
|
&tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify})
|
||
|
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "[warn] '%s' may not be accepting connections: %s\n", remote, err)
|
||
|
} else {
|
||
|
conn.Close()
|
||
|
}
|
||
|
|
||
|
local := opts.LocalAddress + ":" + strconv.Itoa(opts.LocalPort)
|
||
|
ln, err := net.Listen("tcp", local)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
fmt.Fprintf(os.Stdout, "[listening] %s:%d <= %s:%d\n",
|
||
|
opts.RemoteAddress, opts.RemotePort, opts.LocalAddress, opts.LocalPort)
|
||
|
|
||
|
for {
|
||
|
conn, err := ln.Accept()
|
||
|
if nil != err {
|
||
|
fmt.Fprintf(os.Stderr, "[error] %s\n", err)
|
||
|
continue
|
||
|
}
|
||
|
go handleConnection(remote, conn, opts)
|
||
|
}
|
||
|
}
|