mirror of
				https://github.com/therootcompany/sclient
				synced 2024-11-16 17:09:00 +00:00 
			
		
		
		
	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 { | ||||||
|  | 		// We may omit the second argument if we're going straight to stdin | ||||||
|  | 		if stat, _ := os.Stdin.Stat(); 1 == i && (stat.Mode()&os.ModeCharDevice) == 0 { | ||||||
|  | 			localstr = "|" | ||||||
|  | 		} else { | ||||||
| 			usage() | 			usage() | ||||||
| 		os.Exit(0) | 			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,8 +59,13 @@ 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 | ||||||
|  | 		opts.LocalPort = -1 | ||||||
|  | 	} else { | ||||||
|  | 		// Test that argument is a local address | ||||||
|  | 		local := strings.Split(localstr, ":") | ||||||
| 
 | 
 | ||||||
| 		if 1 == len(local) { | 		if 1 == len(local) { | ||||||
| 			lport, err := strconv.Atoi(local[0]) | 			lport, err := strconv.Atoi(local[0]) | ||||||
| @ -72,11 +83,13 @@ func main() { | |||||||
| 			opts.LocalAddress = local[0] | 			opts.LocalAddress = local[0] | ||||||
| 			opts.LocalPort = lport | 			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) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										56
									
								
								sclient.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								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 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	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", | 		fmt.Fprintf(os.Stdout, "[connect] %s => %s:%d\n", | ||||||
| 			strings.Replace(conn.RemoteAddr().String(), "[::1]:", "localhost:", 1), opts.RemoteAddress, opts.RemotePort) | 			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 { | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								tests/get.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/get.bin
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | GET / HTTP/1.1 | ||||||
|  | Host: telebit.cloud | ||||||
|  | Connection: close | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										8
									
								
								tests/localhost.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								tests/localhost.sh
									
									
									
									
									
										Executable file
									
								
							| @ -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 | ||||||
							
								
								
									
										3
									
								
								tests/pipe.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								tests/pipe.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | cat tests/get.bin | go run -race sclient*.go telebit.cloud:443 | ||||||
							
								
								
									
										3
									
								
								tests/stdin.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								tests/stdin.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | go run -race sclient*.go telebit.cloud:443 - < ./tests/get.bin | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user