Skip to content

Commit f5bb373

Browse files
authored
Merge pull request #72 from davrodpin/multiple-remotes
Add support for multiple ssh channels through one connection.
2 parents b590417 + 737a597 commit f5bb373

File tree

17 files changed

+775
-353
lines changed

17 files changed

+775
-353
lines changed

cli/cli.go

+54-20
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import (
1010

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

13-
// App contains main settings of application.
13+
// App contains all supported CLI arguments given by the user.
1414
type App struct {
1515
args []string
1616
flag *flag.FlagSet
1717

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

9696
switch c.Command {
9797
case "new-alias":
98-
if c.Remote.String() == "" {
99-
return fmt.Errorf("required flag is missing: -remote")
100-
} else if c.Server.String() == "" {
98+
if c.Server.String() == "" {
10199
return fmt.Errorf("required flag is missing: -server")
102100
}
103101
case "start":
104102
if c.Server.String() == "" {
105103
return fmt.Errorf("required flag is missing: -server")
106104
}
107-
108105
}
106+
109107
return nil
110108
}
111109

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

131-
// HostInput holds information about a host
132-
type HostInput struct {
129+
// AddressInput holds information about a host
130+
type AddressInput struct {
133131
User string
134132
Host string
135133
Port string
136134
}
137135

138-
// String returns a string representation of a HostInput
139-
func (h HostInput) String() string {
136+
// String returns a string representation of a AddressInput
137+
func (h AddressInput) String() string {
140138
var s string
141139
if h.User == "" {
142140
s = h.Address()
@@ -147,8 +145,8 @@ func (h HostInput) String() string {
147145
return s
148146
}
149147

150-
// Set parses a string representation of HostInput into its proper attributes.
151-
func (h *HostInput) Set(value string) error {
148+
// Set parses a string representation of AddressInput into its proper attributes.
149+
func (h *AddressInput) Set(value string) error {
152150
result := parseServerInput(value)
153151
h.User = strings.Trim(result["user"], "@")
154152
h.Host = result["host"]
@@ -157,9 +155,9 @@ func (h *HostInput) Set(value string) error {
157155
return nil
158156
}
159157

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

181179
return result
182180
}
181+
182+
type AddressInputList []AddressInput
183+
184+
func (il AddressInputList) String() string {
185+
ils := []string{}
186+
187+
for _, i := range il {
188+
ils = append(ils, i.String())
189+
}
190+
191+
return strings.Join(ils, ",")
192+
}
193+
194+
func (il *AddressInputList) Set(value string) error {
195+
i := AddressInput{}
196+
197+
err := i.Set(value)
198+
if err != nil {
199+
return err
200+
}
201+
202+
*il = append(*il, i)
203+
204+
return nil
205+
}
206+
207+
func (il AddressInputList) List() []string {
208+
sl := []string{}
209+
210+
for _, i := range il {
211+
sl = append(sl, i.String())
212+
}
213+
214+
return sl
215+
216+
}

cli/cli_test.go

+26-18
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,52 @@ import (
77
"github.com/davrodpin/mole/cli"
88
)
99

10-
func TestHostInput(t *testing.T) {
10+
func TestAddressInput(t *testing.T) {
1111
tests := []struct {
1212
input string
13-
expected cli.HostInput
13+
expected cli.AddressInput
1414
}{
1515
{
1616
"test",
17-
cli.HostInput{User: "", Host: "test", Port: ""},
17+
cli.AddressInput{User: "", Host: "test", Port: ""},
1818
},
1919
{
2020
"user@test",
21-
cli.HostInput{User: "user", Host: "test", Port: ""},
21+
cli.AddressInput{User: "user", Host: "test", Port: ""},
2222
},
2323
{
2424
"user@test:2222",
25-
cli.HostInput{User: "user", Host: "test", Port: "2222"},
25+
cli.AddressInput{User: "user", Host: "test", Port: "2222"},
2626
},
2727
{
2828
"test-1",
29-
cli.HostInput{User: "", Host: "test-1", Port: ""},
29+
cli.AddressInput{User: "", Host: "test-1", Port: ""},
3030
},
3131
{
3232
"test-1-2-xy",
33-
cli.HostInput{User: "", Host: "test-1-2-xy", Port: ""},
33+
cli.AddressInput{User: "", Host: "test-1-2-xy", Port: ""},
3434
},
3535
{
3636
"test.com",
37-
cli.HostInput{User: "", Host: "test.com", Port: ""},
37+
cli.AddressInput{User: "", Host: "test.com", Port: ""},
3838
},
3939
{
4040
"test_1",
41-
cli.HostInput{User: "", Host: "test_1", Port: ""},
41+
cli.AddressInput{User: "", Host: "test_1", Port: ""},
4242
},
4343
{
4444
"user@test_1",
45-
cli.HostInput{User: "user", Host: "test_1", Port: ""},
45+
cli.AddressInput{User: "user", Host: "test_1", Port: ""},
4646
},
4747
{
4848
"user@test_1:2222",
49-
cli.HostInput{User: "user", Host: "test_1", Port: "2222"},
49+
cli.AddressInput{User: "user", Host: "test_1", Port: "2222"},
5050
},
5151
}
5252

53-
var h cli.HostInput
53+
var h cli.AddressInput
5454
for _, test := range tests {
55-
h = cli.HostInput{}
55+
h = cli.AddressInput{}
5656
h.Set(test.input)
5757

5858
if !reflect.DeepEqual(test.expected, h) {
@@ -128,11 +128,7 @@ func TestValidate(t *testing.T) {
128128
},
129129
{
130130
[]string{"./mole", "-alias", "xyz", "-server", "example1"},
131-
false,
132-
},
133-
{
134-
[]string{"./mole", "-alias", "xyz", "-server", "example1"},
135-
false,
131+
true,
136132
},
137133
{
138134
[]string{"./mole", "-alias", "xyz", "-remote", ":443"},
@@ -142,6 +138,18 @@ func TestValidate(t *testing.T) {
142138
[]string{"./mole", "-alias", "xyz"},
143139
false,
144140
},
141+
{
142+
[]string{"./mole", "-local", ":8080", "-remote", ":80", "-server", "example1"},
143+
true,
144+
},
145+
{
146+
[]string{"./mole", "-remote", ":3366", "-remote", ":443", "-server", "example1"},
147+
true,
148+
},
149+
{
150+
[]string{"./mole", "-local", ":1234", "-remote", ":3366", "-remote", ":443", "-server", "example1"},
151+
true,
152+
},
145153
}
146154

147155
var c *cli.App

cmd/mole/alias.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/davrodpin/mole/cli"
8+
"github.com/davrodpin/mole/storage"
9+
)
10+
11+
func lsAliases() error {
12+
aliases, err := storage.FindAll()
13+
if err != nil {
14+
return err
15+
}
16+
17+
as := []string{}
18+
for a := range aliases {
19+
as = append(as, a)
20+
}
21+
22+
fmt.Printf("alias list: %s\n", strings.Join(as, ", "))
23+
24+
return nil
25+
}
26+
27+
func app2alias(app cli.App) *storage.Alias {
28+
return &storage.Alias{
29+
Local: app.Local.List(),
30+
Remote: app.Remote.List(),
31+
Server: app.Server.String(),
32+
Key: app.Key,
33+
Verbose: app.Verbose,
34+
Help: app.Help,
35+
Version: app.Version,
36+
Detach: app.Detach,
37+
}
38+
}
39+
40+
func alias2app(t *storage.Alias) (*cli.App, error) {
41+
sla, err := t.ReadLocal()
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
lal := cli.AddressInputList{}
47+
for _, la := range sla {
48+
lal.Set(la)
49+
}
50+
51+
sra, err := t.ReadRemote()
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
ral := cli.AddressInputList{}
57+
for _, ra := range sra {
58+
ral.Set(ra)
59+
}
60+
61+
server := cli.AddressInput{}
62+
server.Set(t.Server)
63+
64+
return &cli.App{
65+
Command: "start",
66+
Local: lal,
67+
Remote: ral,
68+
Server: server,
69+
Key: t.Key,
70+
Verbose: t.Verbose,
71+
Help: t.Help,
72+
Version: t.Version,
73+
Detach: t.Detach,
74+
}, nil
75+
}

0 commit comments

Comments
 (0)