support pipes and stdin
This commit is contained in:
parent
2235bf3a55
commit
c786b0bd07
16
README.md
16
README.md
|
@ -96,3 +96,19 @@ Ignore a bad TLS/SSL/HTTPS certificate and connect anyway.
|
||||||
```bash
|
```bash
|
||||||
sclient -k badtls.telebit.cloud:443 localhost:3000
|
sclient -k badtls.telebit.cloud:443 localhost:3000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Reading from stdin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sclient telebit.cloud:443 -
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sclient telebit.cloud:443 - </path/to/file
|
||||||
|
```
|
||||||
|
|
||||||
|
Piping
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "GET / HTTP/1.1\r\nHost: telebit.cloud\r\n\r\n" | sclient telebit.cloud:443
|
||||||
|
```
|
||||||
|
|
|
@ -25,12 +25,18 @@ func main() {
|
||||||
insecure := flag.Bool("k", false, "ignore bad TLS/SSL/HTTPS certificates")
|
insecure := flag.Bool("k", false, "ignore bad TLS/SSL/HTTPS certificates")
|
||||||
flag.BoolVar(insecure, "insecure", false, "ignore bad TLS/SSL/HTTPS certificates")
|
flag.BoolVar(insecure, "insecure", false, "ignore bad TLS/SSL/HTTPS certificates")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
remotestr := flag.Arg(0)
|
||||||
|
localstr := flag.Arg(1)
|
||||||
|
|
||||||
// NArg, Arg, Args
|
|
||||||
i := flag.NArg()
|
i := flag.NArg()
|
||||||
if 2 != i {
|
if 2 != i {
|
||||||
usage()
|
// We may omit the second argument if we're going straight to stdin
|
||||||
os.Exit(0)
|
if stat, _ := os.Stdin.Stat(); 1 == i && (stat.Mode()&os.ModeCharDevice) == 0 {
|
||||||
|
localstr = "|"
|
||||||
|
} else {
|
||||||
|
usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := &SclientOpts{}
|
opts := &SclientOpts{}
|
||||||
|
@ -38,8 +44,8 @@ func main() {
|
||||||
opts.LocalAddress = "localhost"
|
opts.LocalAddress = "localhost"
|
||||||
opts.InsecureSkipVerify = *insecure
|
opts.InsecureSkipVerify = *insecure
|
||||||
|
|
||||||
remote := strings.Split(flag.Arg(0), ":")
|
remote := strings.Split(remotestr, ":")
|
||||||
//remoteAddr, remotePort, err := net.SplitHostPort(flag.Arg(0))
|
//remoteAddr, remotePort, err := net.SplitHostPort(remotestr)
|
||||||
if 2 == len(remote) {
|
if 2 == len(remote) {
|
||||||
rport, err := strconv.Atoi(remote[1])
|
rport, err := strconv.Atoi(remote[1])
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -53,30 +59,37 @@ func main() {
|
||||||
}
|
}
|
||||||
opts.RemoteAddress = remote[0]
|
opts.RemoteAddress = remote[0]
|
||||||
|
|
||||||
local := strings.Split(flag.Arg(1), ":")
|
if "-" == localstr || "|" == localstr {
|
||||||
//localAddr, localPort, err := net.SplitHostPort(flag.Arg(0))
|
// User may specify stdin/stdout instead of net
|
||||||
|
opts.LocalAddress = localstr
|
||||||
if 1 == len(local) {
|
opts.LocalPort = -1
|
||||||
lport, err := strconv.Atoi(local[0])
|
|
||||||
if nil != err {
|
|
||||||
usage()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
opts.LocalPort = lport
|
|
||||||
} else {
|
} else {
|
||||||
lport, err := strconv.Atoi(local[1])
|
// Test that argument is a local address
|
||||||
if nil != err {
|
local := strings.Split(localstr, ":")
|
||||||
usage()
|
|
||||||
os.Exit(0)
|
if 1 == len(local) {
|
||||||
|
lport, err := strconv.Atoi(local[0])
|
||||||
|
if nil != err {
|
||||||
|
usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
opts.LocalPort = lport
|
||||||
|
} else {
|
||||||
|
lport, err := strconv.Atoi(local[1])
|
||||||
|
if nil != err {
|
||||||
|
usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
opts.LocalAddress = local[0]
|
||||||
|
opts.LocalPort = lport
|
||||||
}
|
}
|
||||||
opts.LocalAddress = local[0]
|
|
||||||
opts.LocalPort = lport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sclient := &Sclient{}
|
sclient := &Sclient{}
|
||||||
err := sclient.DialAndListen(opts)
|
err := sclient.DialAndListen(opts)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
usage()
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
os.Exit(0)
|
//usage()
|
||||||
|
//os.Exit(6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
60
sclient.go
60
sclient.go
|
@ -10,6 +10,36 @@ import (
|
||||||
"strings"
|
"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 {
|
type SclientOpts struct {
|
||||||
RemoteAddress string
|
RemoteAddress string
|
||||||
RemotePort int
|
RemotePort int
|
||||||
|
@ -20,13 +50,13 @@ type SclientOpts struct {
|
||||||
|
|
||||||
type Sclient struct{}
|
type Sclient struct{}
|
||||||
|
|
||||||
func pipe(r net.Conn, w net.Conn, t string) {
|
func pipe(r Rwc, w Rwc, t string) {
|
||||||
buffer := make([]byte, 2048)
|
buffer := make([]byte, 2048)
|
||||||
for {
|
for {
|
||||||
done := false
|
done := false
|
||||||
// NOTE: count may be > 0 even if there's an err
|
// 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)
|
//fmt.Fprintf(os.Stdout, "[debug] (%s) reading\n", t)
|
||||||
|
count, err := r.Read(buffer)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
//fmt.Fprintf(os.Stdout, "[debug] (%s:%d) error reading %s\n", t, count, err)
|
//fmt.Fprintf(os.Stdout, "[debug] (%s:%d) error reading %s\n", t, count, err)
|
||||||
if io.EOF != err {
|
if io.EOF != err {
|
||||||
|
@ -56,7 +86,7 @@ func pipe(r net.Conn, w net.Conn, t string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleConnection(remote string, conn net.Conn, opts *SclientOpts) {
|
func handleConnection(remote string, conn Rwc, opts *SclientOpts) {
|
||||||
sclient, err := tls.Dial("tcp", remote,
|
sclient, err := tls.Dial("tcp", remote,
|
||||||
&tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify})
|
&tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify})
|
||||||
|
|
||||||
|
@ -66,8 +96,13 @@ func handleConnection(remote string, conn net.Conn, opts *SclientOpts) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "[connect] %s => %s:%d\n",
|
if "stdio" == conn.RemoteAddr().Network() {
|
||||||
strings.Replace(conn.RemoteAddr().String(), "[::1]:", "localhost:", 1), opts.RemoteAddress, opts.RemotePort)
|
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")
|
go pipe(conn, sclient, "local")
|
||||||
pipe(sclient, conn, "remote")
|
pipe(sclient, conn, "remote")
|
||||||
|
@ -84,6 +119,21 @@ func (*Sclient) DialAndListen(opts *SclientOpts) error {
|
||||||
conn.Close()
|
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)
|
local := opts.LocalAddress + ":" + strconv.Itoa(opts.LocalPort)
|
||||||
ln, err := net.Listen("tcp", local)
|
ln, err := net.Listen("tcp", local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
GET / HTTP/1.1
|
||||||
|
Host: telebit.cloud
|
||||||
|
Connection: close
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go run -race sclient*.go telebit.cloud:443 localhost:3000 &
|
||||||
|
my_pid=$!
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
netcat localhost 3000 < tests/get.bin
|
||||||
|
kill $my_pid
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cat tests/get.bin | go run -race sclient*.go telebit.cloud:443
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go run -race sclient*.go telebit.cloud:443 - < ./tests/get.bin
|
Loading…
Reference in New Issue