Mirror von https://github.com/therootcompany/sclient
Commit
224d005a90
4 geänderte Dateien mit 308 neuen und 0 gelöschten Zeilen
@ -0,0 +1,41 @@ |
|||
Copyright 2018 AJ ONeal |
|||
|
|||
This is open source software; you can redistribute it and/or modify it under the |
|||
terms of either: |
|||
|
|||
a) the "MIT License" |
|||
b) the "Apache-2.0 License" |
|||
|
|||
MIT License |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
|
|||
Apache-2.0 License Summary |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
@ -0,0 +1,81 @@ |
|||
sclient.go |
|||
========== |
|||
|
|||
Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. |
|||
|
|||
Also ideal for multiplexing a single port with multiple protocols using SNI. |
|||
|
|||
Unwrap a TLS connection: |
|||
|
|||
```bash |
|||
$ sclient whatever.com:443 localhost:3000 |
|||
> [listening] telebit.cloud:443 <= localhost:3000 |
|||
``` |
|||
|
|||
Connect via Telnet |
|||
|
|||
```bash |
|||
$ telnet localhost 3000 |
|||
``` |
|||
|
|||
Connect via netcat (nc) |
|||
|
|||
```bash |
|||
$ nc localhost 3000 |
|||
``` |
|||
|
|||
A poor man's (or Windows user's) makeshift replacement for `openssl s_client`, `stunnel`, or `socat`. |
|||
|
|||
Install |
|||
======= |
|||
|
|||
### macOS, Linux, Windows |
|||
|
|||
For the moment you'll have to install go and compile `sclient` yourself: |
|||
|
|||
* <https://golang.org/doc/install#install> |
|||
|
|||
```bash |
|||
git clone |
|||
go build sclient*.go |
|||
rsync -av sclient-cli /usr/local/bin/sclient |
|||
``` |
|||
|
|||
```bash |
|||
go run sclient*.go example.com:443 localhost:3000 |
|||
``` |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
```bash |
|||
sclient <remote> <local> [-k | --insecure] |
|||
``` |
|||
|
|||
* remote |
|||
* must have servername (i.e. example.com) |
|||
* port is optional (default is 443) |
|||
* local |
|||
* address is optional (default is localhost) |
|||
* must have port (i.e. 3000) |
|||
|
|||
Examples |
|||
======== |
|||
|
|||
Bridge between `telebit.cloud` and local port `3000`. |
|||
|
|||
```bash |
|||
sclient telebit.cloud 3000 |
|||
``` |
|||
|
|||
Same as above, but more explicit |
|||
|
|||
```bash |
|||
sclient telebit.cloud:443 localhost:3000 |
|||
``` |
|||
|
|||
Ignore a bad TLS/SSL/HTTPS certificate and connect anyway. |
|||
|
|||
```bash |
|||
sclient badtls.telebit.cloud:443 localhost:3000 -k |
|||
``` |
@ -0,0 +1,82 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"flag" |
|||
"fmt" |
|||
"os" |
|||
"strconv" |
|||
"strings" |
|||
) |
|||
|
|||
func usage() { |
|||
fmt.Fprintf(os.Stderr, "\nusage: go run sclient*.go <remote> <local>\n"+ |
|||
"\n"+ |
|||
" ex: sclient example.com 3000\n"+ |
|||
" (sclient example.com:443 localhost:3000)\n"+ |
|||
"\n"+ |
|||
" ex: sclient example.com:8443 0.0.0.0:4080\n"+ |
|||
"\n") |
|||
flag.PrintDefaults() |
|||
fmt.Println() |
|||
} |
|||
|
|||
func main() { |
|||
flag.Usage = usage |
|||
insecure := flag.Bool("k", false, "ignore bad TLS/SSL/HTTPS certificates") |
|||
flag.BoolVar(insecure, "insecure", false, "ignore bad TLS/SSL/HTTPS certificates") |
|||
flag.Parse() |
|||
|
|||
// NArg, Arg, Args
|
|||
i := flag.NArg() |
|||
if 2 != i { |
|||
usage() |
|||
os.Exit(0) |
|||
} |
|||
|
|||
opts := &SclientOpts{} |
|||
opts.RemotePort = 443 |
|||
opts.LocalAddress = "localhost" |
|||
opts.InsecureSkipVerify = *insecure |
|||
|
|||
remote := strings.Split(flag.Arg(0), ":") |
|||
//remoteAddr, remotePort, err := net.SplitHostPort(flag.Arg(0))
|
|||
if 2 == len(remote) { |
|||
rport, err := strconv.Atoi(remote[1]) |
|||
if nil != err { |
|||
usage() |
|||
os.Exit(0) |
|||
} |
|||
opts.RemotePort = rport |
|||
} else if 1 != len(remote) { |
|||
usage() |
|||
os.Exit(0) |
|||
} |
|||
opts.RemoteAddress = remote[0] |
|||
|
|||
local := strings.Split(flag.Arg(1), ":") |
|||
//localAddr, localPort, err := net.SplitHostPort(flag.Arg(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 |
|||
} |
|||
|
|||
sclient := &Sclient{} |
|||
err := sclient.DialAndListen(opts) |
|||
if nil != err { |
|||
usage() |
|||
os.Exit(0) |
|||
} |
|||
} |
@ -0,0 +1,104 @@ |
|||
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) |
|||
} |
|||
} |
Laden…
In neuem Issue referenzieren