réplica de
				https://github.com/therootcompany/sclient
				synced 2025-11-04 07:12:48 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			155 líneas
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			155 líneas
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/tls"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// I wonder if I can get this to exactly mirror UnixAddr without passing it in
 | 
						|
type stdaddr struct {
 | 
						|
	net.UnixAddr
 | 
						|
}
 | 
						|
 | 
						|
type stdnet struct {
 | 
						|
	in   *os.File // os.Stdin
 | 
						|
	out  *os.File // os.Stdout
 | 
						|
	addr *stdaddr
 | 
						|
}
 | 
						|
 | 
						|
func (rw *stdnet) Read(buf []byte) (n int, err error) {
 | 
						|
	return rw.in.Read(buf)
 | 
						|
}
 | 
						|
func (rw *stdnet) Write(buf []byte) (n int, err error) {
 | 
						|
	return rw.out.Write(buf)
 | 
						|
}
 | 
						|
func (rw *stdnet) Close() error {
 | 
						|
	return rw.in.Close()
 | 
						|
}
 | 
						|
func (rw *stdnet) RemoteAddr() net.Addr {
 | 
						|
	return rw.addr
 | 
						|
}
 | 
						|
 | 
						|
// not all of net.Conn, just RWC and RemoteAddr()
 | 
						|
type Rwc interface {
 | 
						|
	io.ReadWriteCloser
 | 
						|
	RemoteAddr() net.Addr
 | 
						|
}
 | 
						|
 | 
						|
type SclientOpts struct {
 | 
						|
	RemoteAddress      string
 | 
						|
	RemotePort         int
 | 
						|
	LocalAddress       string
 | 
						|
	LocalPort          int
 | 
						|
	InsecureSkipVerify bool
 | 
						|
}
 | 
						|
 | 
						|
type Sclient struct{}
 | 
						|
 | 
						|
func pipe(r Rwc, w Rwc, t string) {
 | 
						|
	buffer := make([]byte, 2048)
 | 
						|
	for {
 | 
						|
		done := false
 | 
						|
		// NOTE: count may be > 0 even if there's an err
 | 
						|
		//fmt.Fprintf(os.Stdout, "[debug] (%s) reading\n", t)
 | 
						|
		count, err := r.Read(buffer)
 | 
						|
		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 Rwc, 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
 | 
						|
	}
 | 
						|
 | 
						|
	if "stdio" == conn.RemoteAddr().Network() {
 | 
						|
		fmt.Fprintf(os.Stdout, "(connected to %s:%d and reading from %s)\n",
 | 
						|
			opts.RemoteAddress, opts.RemotePort, conn.RemoteAddr().String())
 | 
						|
	} else {
 | 
						|
		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()
 | 
						|
	}
 | 
						|
 | 
						|
	// use stdin/stdout
 | 
						|
	if "-" == opts.LocalAddress || "|" == opts.LocalAddress {
 | 
						|
		var name string
 | 
						|
		network := "stdio"
 | 
						|
		if "|" == opts.LocalAddress {
 | 
						|
			name = "pipe"
 | 
						|
		} else {
 | 
						|
			name = "stdin"
 | 
						|
		}
 | 
						|
		conn := &stdnet{os.Stdin, os.Stdout, &stdaddr{net.UnixAddr{name, network}}}
 | 
						|
		handleConnection(remote, conn, opts)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// use net.Conn
 | 
						|
	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)
 | 
						|
	}
 | 
						|
}
 |