Skip to content

Commit

Permalink
Add support for multiple ssh channels through one connection.
Browse files Browse the repository at this point in the history
This change allows the user to specify multiple remote addresses, so the
same ssh connection can be leveraged to establish multiple tunnels by
creating extra ssh tunnel for each additional remote address given.
  • Loading branch information
David Pinheiro committed Jun 24, 2019
1 parent b590417 commit 737a597
Show file tree
Hide file tree
Showing 17 changed files with 775 additions and 353 deletions.
74 changes: 54 additions & 20 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import (

var re = regexp.MustCompile(`(?P<user>.+@)?(?P<host>[[:alpha:][:digit:]\_\-\.]+)?(?P<port>:[0-9]+)?`)

// App contains main settings of application.
// App contains all supported CLI arguments given by the user.
type App struct {
args []string
flag *flag.FlagSet

Command string
Local HostInput
Remote HostInput
Server HostInput
Local AddressInputList
Remote AddressInputList
Server AddressInput
Key string
Verbose bool
Help bool
Expand Down Expand Up @@ -47,8 +47,8 @@ func (c *App) Parse() error {
f.BoolVar(&c.AliasDelete, "delete", false, "delete a tunnel alias (must be used with -alias)")
f.BoolVar(&c.AliasList, "aliases", false, "list all aliases")
f.StringVar(&c.Start, "start", "", "Start a tunnel using a given alias")
f.Var(&c.Local, "local", "(optional) Set local endpoint address: [<host>]:<port>")
f.Var(&c.Remote, "remote", "set remote endpoint address: [<host>]:<port>")
f.Var(&c.Local, "local", "(optional) Set local endpoint address: [<host>]:<port>. Multiple -local args can be provided.")
f.Var(&c.Remote, "remote", "(optional) Set remote endpoint address: [<host>]:<port>. Multiple -remote args can be provided.")
f.Var(&c.Server, "server", "set server address: [<user>@]<host>[:<port>]")
f.StringVar(&c.Key, "key", "", "(optional) Set server authentication key file path")
f.BoolVar(&c.Verbose, "v", false, "(optional) Increase log verbosity")
Expand Down Expand Up @@ -95,26 +95,24 @@ func (c App) Validate() error {

switch c.Command {
case "new-alias":
if c.Remote.String() == "" {
return fmt.Errorf("required flag is missing: -remote")
} else if c.Server.String() == "" {
if c.Server.String() == "" {
return fmt.Errorf("required flag is missing: -server")
}
case "start":
if c.Server.String() == "" {
return fmt.Errorf("required flag is missing: -server")
}

}

return nil
}

// PrintUsage prints, to the standard output, the informational text on how to
// use the tool.
func (c *App) PrintUsage() {
fmt.Fprintf(os.Stderr, "%s\n\n", `usage:
mole [-v] [-insecure] [-detach] [-local [<host>]:<port>] -remote [<host>]:<port> -server [<user>@]<host>[:<port>] [-key <key_path>]
mole -alias <alias_name> [-v] [-local [<host>]:<port>] -remote [<host>]:<port> -server [<user>@]<host>[:<port>] [-key <key_path>]
mole [-v] [-insecure] [-detach] (-local [<host>]:<port>)... (-remote [<host>]:<port>)... -server [<user>@]<host>[:<port>] [-key <key_path>]
mole -alias <alias_name> [-v] (-local [<host>]:<port>)... (-remote [<host>]:<port>)... -server [<user>@]<host>[:<port>] [-key <key_path>]
mole -alias <alias_name> -delete
mole -start <alias_name>
mole -help
Expand All @@ -128,15 +126,15 @@ func (c App) String() string {
c.Local, c.Remote, c.Server, c.Key, c.Verbose, c.Help, c.Version, c.Detach)
}

// HostInput holds information about a host
type HostInput struct {
// AddressInput holds information about a host
type AddressInput struct {
User string
Host string
Port string
}

// String returns a string representation of a HostInput
func (h HostInput) String() string {
// String returns a string representation of a AddressInput
func (h AddressInput) String() string {
var s string
if h.User == "" {
s = h.Address()
Expand All @@ -147,8 +145,8 @@ func (h HostInput) String() string {
return s
}

// Set parses a string representation of HostInput into its proper attributes.
func (h *HostInput) Set(value string) error {
// Set parses a string representation of AddressInput into its proper attributes.
func (h *AddressInput) Set(value string) error {
result := parseServerInput(value)
h.User = strings.Trim(result["user"], "@")
h.Host = result["host"]
Expand All @@ -157,9 +155,9 @@ func (h *HostInput) Set(value string) error {
return nil
}

// Address returns a string representation of HostInput to be used to perform
// Address returns a string representation of AddressInput to be used to perform
// network connections.
func (h HostInput) Address() string {
func (h AddressInput) Address() string {
if h.Port == "" {
return fmt.Sprintf("%s", h.Host)
}
Expand All @@ -180,3 +178,39 @@ func parseServerInput(input string) map[string]string {

return result
}

type AddressInputList []AddressInput

func (il AddressInputList) String() string {
ils := []string{}

for _, i := range il {
ils = append(ils, i.String())
}

return strings.Join(ils, ",")
}

func (il *AddressInputList) Set(value string) error {
i := AddressInput{}

err := i.Set(value)
if err != nil {
return err
}

*il = append(*il, i)

return nil
}

func (il AddressInputList) List() []string {
sl := []string{}

for _, i := range il {
sl = append(sl, i.String())
}

return sl

}
44 changes: 26 additions & 18 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,52 @@ import (
"github.com/davrodpin/mole/cli"
)

func TestHostInput(t *testing.T) {
func TestAddressInput(t *testing.T) {
tests := []struct {
input string
expected cli.HostInput
expected cli.AddressInput
}{
{
"test",
cli.HostInput{User: "", Host: "test", Port: ""},
cli.AddressInput{User: "", Host: "test", Port: ""},
},
{
"user@test",
cli.HostInput{User: "user", Host: "test", Port: ""},
cli.AddressInput{User: "user", Host: "test", Port: ""},
},
{
"user@test:2222",
cli.HostInput{User: "user", Host: "test", Port: "2222"},
cli.AddressInput{User: "user", Host: "test", Port: "2222"},
},
{
"test-1",
cli.HostInput{User: "", Host: "test-1", Port: ""},
cli.AddressInput{User: "", Host: "test-1", Port: ""},
},
{
"test-1-2-xy",
cli.HostInput{User: "", Host: "test-1-2-xy", Port: ""},
cli.AddressInput{User: "", Host: "test-1-2-xy", Port: ""},
},
{
"test.com",
cli.HostInput{User: "", Host: "test.com", Port: ""},
cli.AddressInput{User: "", Host: "test.com", Port: ""},
},
{
"test_1",
cli.HostInput{User: "", Host: "test_1", Port: ""},
cli.AddressInput{User: "", Host: "test_1", Port: ""},
},
{
"user@test_1",
cli.HostInput{User: "user", Host: "test_1", Port: ""},
cli.AddressInput{User: "user", Host: "test_1", Port: ""},
},
{
"user@test_1:2222",
cli.HostInput{User: "user", Host: "test_1", Port: "2222"},
cli.AddressInput{User: "user", Host: "test_1", Port: "2222"},
},
}

var h cli.HostInput
var h cli.AddressInput
for _, test := range tests {
h = cli.HostInput{}
h = cli.AddressInput{}
h.Set(test.input)

if !reflect.DeepEqual(test.expected, h) {
Expand Down Expand Up @@ -128,11 +128,7 @@ func TestValidate(t *testing.T) {
},
{
[]string{"./mole", "-alias", "xyz", "-server", "example1"},
false,
},
{
[]string{"./mole", "-alias", "xyz", "-server", "example1"},
false,
true,
},
{
[]string{"./mole", "-alias", "xyz", "-remote", ":443"},
Expand All @@ -142,6 +138,18 @@ func TestValidate(t *testing.T) {
[]string{"./mole", "-alias", "xyz"},
false,
},
{
[]string{"./mole", "-local", ":8080", "-remote", ":80", "-server", "example1"},
true,
},
{
[]string{"./mole", "-remote", ":3366", "-remote", ":443", "-server", "example1"},
true,
},
{
[]string{"./mole", "-local", ":1234", "-remote", ":3366", "-remote", ":443", "-server", "example1"},
true,
},
}

var c *cli.App
Expand Down
75 changes: 75 additions & 0 deletions cmd/mole/alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"fmt"
"strings"

"github.com/davrodpin/mole/cli"
"github.com/davrodpin/mole/storage"
)

func lsAliases() error {
aliases, err := storage.FindAll()
if err != nil {
return err
}

as := []string{}
for a := range aliases {
as = append(as, a)
}

fmt.Printf("alias list: %s\n", strings.Join(as, ", "))

return nil
}

func app2alias(app cli.App) *storage.Alias {
return &storage.Alias{
Local: app.Local.List(),
Remote: app.Remote.List(),
Server: app.Server.String(),
Key: app.Key,
Verbose: app.Verbose,
Help: app.Help,
Version: app.Version,
Detach: app.Detach,
}
}

func alias2app(t *storage.Alias) (*cli.App, error) {
sla, err := t.ReadLocal()
if err != nil {
return nil, err
}

lal := cli.AddressInputList{}
for _, la := range sla {
lal.Set(la)
}

sra, err := t.ReadRemote()
if err != nil {
return nil, err
}

ral := cli.AddressInputList{}
for _, ra := range sra {
ral.Set(ra)
}

server := cli.AddressInput{}
server.Set(t.Server)

return &cli.App{
Command: "start",
Local: lal,
Remote: ral,
Server: server,
Key: t.Key,
Verbose: t.Verbose,
Help: t.Help,
Version: t.Version,
Detach: t.Detach,
}, nil
}
Loading

0 comments on commit 737a597

Please sign in to comment.