Skip to content

Commit 50bf866

Browse files
committed
Merge branch 'develop'
2 parents b6fb0be + 576791e commit 50bf866

11 files changed

Lines changed: 296 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
dist
2+
.idea

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ cover: build-cover
4949
go tool covdata textfmt -i=$(COVER_DIR) -o $(COVER_LINES)
5050
@# unit tests
5151
tmpfile=$(mktemp)
52-
go test -coverprofile=tmpfile ./internal/... > /dev/null
52+
go test -coverprofile=tmpfile ./cmd/... ./internal/... > /dev/null
5353
@# combine
5454
tail -n +2 tmpfile >> $(COVER_LINES)
5555
rm -f tmpfile

cmd/boring/status_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"testing"
6+
"time"
7+
8+
"github.com/alebeck/boring/internal/log"
9+
"github.com/alebeck/boring/internal/tunnel"
10+
)
11+
12+
func TestStatusClosed(t *testing.T) {
13+
d := &tunnel.Desc{Status: tunnel.Closed}
14+
if s := status(d); s != "closed" {
15+
t.Fatalf("incorrect status: %s", s)
16+
}
17+
}
18+
19+
func TestStatusReconn(t *testing.T) {
20+
d := &tunnel.Desc{Status: tunnel.Reconn}
21+
if s := status(d); s != "reconn" {
22+
t.Fatalf("incorrect status: %s", s)
23+
}
24+
}
25+
26+
func TestStatusUptimeMins(t *testing.T) {
27+
log.Init(io.Discard, true, false)
28+
l := 7*time.Minute + 21*time.Second
29+
d := &tunnel.Desc{
30+
Status: tunnel.Open,
31+
LastConn: time.Now().Add(-l),
32+
}
33+
if s := status(d); s != "07m21s" {
34+
t.Fatalf("incorrect uptime: %s", s)
35+
}
36+
}
37+
38+
func TestStatusUptimeHours(t *testing.T) {
39+
log.Init(io.Discard, true, false)
40+
l := 3*time.Hour + 42*time.Minute
41+
d := &tunnel.Desc{
42+
Status: tunnel.Open,
43+
LastConn: time.Now().Add(-l),
44+
}
45+
if s := status(d); s != "03h42m" {
46+
t.Fatalf("incorrect uptime: %s", s)
47+
}
48+
}
49+
50+
func TestStatusUptimeDays(t *testing.T) {
51+
log.Init(io.Discard, true, false)
52+
l := 2*24*time.Hour + 3*time.Hour
53+
d := &tunnel.Desc{
54+
Status: tunnel.Open,
55+
LastConn: time.Now().Add(-l),
56+
}
57+
if s := status(d); s != "02d03h" {
58+
t.Fatalf("incorrect uptime: %s", s)
59+
}
60+
}

internal/daemon/daemon.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (d *daemon) closeTunnel(conn net.Conn, q *tunnel.Desc) {
122122
d.mutex.RUnlock()
123123
if !ok {
124124
err = fmt.Errorf("tunnel not running")
125-
log.Errorf("%v: could not close tunnel: %v", t.Name, err)
125+
log.Errorf("%v: could not close tunnel: %v", q.Name, err)
126126
return
127127
}
128128

@@ -134,8 +134,8 @@ func (d *daemon) closeTunnel(conn net.Conn, q *tunnel.Desc) {
134134
}
135135

136136
func (d *daemon) listTunnels(conn net.Conn) {
137-
m := make(map[string]tunnel.Desc, len(d.tunnels))
138137
d.mutex.RLock()
138+
m := make(map[string]tunnel.Desc, len(d.tunnels))
139139
for n, t := range d.tunnels {
140140
m[n] = *t.Desc
141141
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package ssh_config
2+
3+
import (
4+
"net"
5+
"reflect"
6+
"testing"
7+
8+
"golang.org/x/crypto/ssh"
9+
"golang.org/x/crypto/ssh/knownhosts"
10+
)
11+
12+
type rsaTestKey struct{}
13+
14+
func (k *rsaTestKey) Type() string {
15+
return ssh.KeyAlgoRSA
16+
}
17+
18+
func (k *rsaTestKey) Marshal() []byte {
19+
return []byte{}
20+
}
21+
22+
func (k *rsaTestKey) Verify(_ []byte, _ *ssh.Signature) error {
23+
return nil
24+
}
25+
26+
type edTestKey struct{}
27+
28+
func (k *edTestKey) Type() string {
29+
return ssh.KeyAlgoED25519
30+
}
31+
32+
func (k *edTestKey) Marshal() []byte {
33+
return []byte{}
34+
}
35+
36+
func (k *edTestKey) Verify(_ []byte, _ *ssh.Signature) error {
37+
return nil
38+
}
39+
40+
// Tests that extractHostKeyAlgos correctly extracts algorithms from a
41+
// HostKeyCallback that returns a KeyError with known keys.
42+
func TestExtractHostKeyAlgos(t *testing.T) {
43+
cb := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
44+
return &knownhosts.KeyError{
45+
Want: []knownhosts.KnownKey{
46+
{Key: &edTestKey{}},
47+
{Key: &rsaTestKey{}},
48+
{Key: nil}, // should be ignored
49+
},
50+
}
51+
}
52+
algos := extractHostKeyAlgos(cb, "example.com")
53+
if !reflect.DeepEqual(algos, []string{
54+
ssh.KeyAlgoED25519,
55+
ssh.KeyAlgoRSA,
56+
ssh.KeyAlgoRSASHA256,
57+
ssh.KeyAlgoRSASHA512,
58+
}) {
59+
t.Errorf("unexpected algorithms: %v", algos)
60+
}
61+
}

internal/tunnel/mode_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package tunnel
2+
3+
import "testing"
4+
5+
func TestModeUnmarshalInvalid(t *testing.T) {
6+
var m Mode
7+
data := "invalid"
8+
if err := m.UnmarshalTOML(data); err == nil || err.Error() != "invalid mode" {
9+
t.Errorf("incorrect error: %v", err)
10+
}
11+
}
12+
13+
func TestModeUnmarshalInvalidType(t *testing.T) {
14+
var m Mode
15+
data := 1
16+
if err := m.UnmarshalTOML(data); err == nil || err.Error() != "invalid mode type" {
17+
t.Errorf("incorrect error: %v", err)
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tunnel
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestStringOrIntInvalidType(t *testing.T) {
9+
var s StringOrInt
10+
if err := s.UnmarshalTOML(struct{}{}); err == nil ||
11+
!strings.Contains(err.Error(), "unsupported type") {
12+
t.Errorf("incorrect error: %v", err)
13+
}
14+
}

test/e2e/daemon_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package e2e
22

33
import (
4+
"io"
5+
"net"
46
"os"
57
"regexp"
68
"strconv"
79
"strings"
810
"syscall"
911
"testing"
1012
"time"
13+
14+
"github.com/alebeck/boring/internal/daemon"
15+
"github.com/alebeck/boring/internal/ipc"
16+
"github.com/alebeck/boring/internal/log"
17+
"github.com/alebeck/boring/internal/tunnel"
1118
)
1219

1320
func pidRunning(pid int) bool {
@@ -60,6 +67,11 @@ func testDaemonLaunch(t *testing.T, env []string) {
6067

6168
// Finally check for graceful termination
6269
time.Sleep(50 * time.Millisecond)
70+
71+
if pidRunning(pid) {
72+
t.Fatalf("pid %d running", pid)
73+
}
74+
6375
sock := getEnv(env, "BORING_SOCK")
6476
if _, err = os.Stat(sock); err == nil {
6577
t.Fatalf("sock file exists after termination: %v", err)
@@ -102,3 +114,68 @@ func TestDaemonLaunchBadSocket(t *testing.T) {
102114

103115
testDaemonLaunch(t, env)
104116
}
117+
118+
func TestDaemonInvalidCommand(t *testing.T) {
119+
env, cancel, err := makeDefaultEnvWithDaemon(t)
120+
if err != nil {
121+
t.Fatalf(err.Error())
122+
}
123+
defer cancel()
124+
125+
// Need this for IPC functions called below
126+
log.Init(io.Discard, false, false)
127+
128+
cmd := daemon.Cmd{Kind: daemon.CmdKind(99)}
129+
conn, err := net.Dial("unix", getEnv(env, "BORING_SOCK"))
130+
if err != nil {
131+
t.Fatalf("could not connect to daemon")
132+
}
133+
defer conn.Close()
134+
135+
if err = ipc.Write(cmd, conn); err != nil {
136+
t.Fatalf(err.Error())
137+
}
138+
var r daemon.Resp
139+
if err = ipc.Read(&r, conn); err != nil {
140+
t.Fatalf(err.Error())
141+
}
142+
143+
if r.Success || !strings.Contains(r.Error, "unknown command") {
144+
t.Fatalf("did not get correct error message: %v", r.Error)
145+
}
146+
}
147+
148+
func TestDaemonInvalidTunnel(t *testing.T) {
149+
env, cancel, err := makeDefaultEnvWithDaemon(t)
150+
if err != nil {
151+
t.Fatalf(err.Error())
152+
}
153+
defer cancel()
154+
155+
// Need this for IPC functions called below
156+
log.Init(io.Discard, false, false)
157+
158+
tun := tunnel.Desc{Name: "notrunning"}
159+
cmd := daemon.Cmd{Kind: daemon.Close, Tunnel: tun}
160+
conn, err := net.Dial("unix", getEnv(env, "BORING_SOCK"))
161+
if err != nil {
162+
t.Fatalf("could not connect to daemon")
163+
}
164+
defer conn.Close()
165+
166+
if err = ipc.Write(cmd, conn); err != nil {
167+
t.Fatalf(err.Error())
168+
}
169+
var r daemon.Resp
170+
if err = ipc.Read(&r, conn); err != nil {
171+
t.Fatalf(err.Error())
172+
}
173+
174+
if r.Success || !strings.Contains(r.Error, "tunnel not running") {
175+
t.Fatalf("did not get correct error message: %v", r.Error)
176+
}
177+
}
178+
179+
func TestDaemonFailSetupListener(t *testing.T) {
180+
// TODO
181+
}

test/e2e/ssh_server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package e2e
22

33
import (
44
"fmt"
5-
"golang.org/x/crypto/ssh"
65
"io"
76
"net"
87
"os"
98
"sync"
109
"time"
10+
11+
"golang.org/x/crypto/ssh"
1112
)
1213

1314
const (
Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package e2e
22

33
import (
44
"fmt"
5-
"golang.org/x/sync/errgroup"
65
"log"
76
"net"
87
"os"
@@ -13,6 +12,7 @@ import (
1312
"time"
1413

1514
xproxy "golang.org/x/net/proxy"
15+
"golang.org/x/sync/errgroup"
1616
)
1717

1818
var server *sshServer
@@ -56,6 +56,33 @@ func TestList(t *testing.T) {
5656
}
5757
}
5858

59+
func TestListNoTunnels(t *testing.T) {
60+
cfg := defaultConfig
61+
cfg.boringConfig = t.TempDir() + "/config.toml"
62+
f, err := os.Create(cfg.boringConfig)
63+
if err != nil {
64+
t.Fatalf("could not create config file: %v", err)
65+
}
66+
f.Close()
67+
68+
env, cancel, err := makeEnvWithDaemon(cfg, t)
69+
if err != nil {
70+
t.Fatalf(err.Error())
71+
}
72+
defer cancel()
73+
74+
c, out, err := cliCommand(env, "list")
75+
if err != nil {
76+
t.Fatalf("failed to run CLI command: %v", err)
77+
}
78+
if c != 0 {
79+
t.Fatalf("exit code %d: %s", c, out)
80+
}
81+
if !strings.Contains(out, "No tunnels configured.") {
82+
t.Fatalf("did not get expected output: %v", out)
83+
}
84+
}
85+
5986
func TestOpen(t *testing.T) {
6087
env, cancel, err := makeDefaultEnvWithDaemon(t)
6188
if err != nil {
@@ -408,7 +435,7 @@ func TestTunnelLocal(t *testing.T) {
408435
}
409436

410437
// Test handling of multiple simultaneous connections
411-
func TestTunnelMulti(t *testing.T) {
438+
func TestTunnelMultiConns(t *testing.T) {
412439
env, cancel, err := makeDefaultEnvWithDaemon(t)
413440
if err != nil {
414441
t.Fatalf(err.Error())
@@ -450,6 +477,30 @@ func TestTunnelMulti(t *testing.T) {
450477
}
451478
}
452479

480+
func TestTunnelMultiTunnels(t *testing.T) {
481+
env, cancel, err := makeDefaultEnvWithDaemon(t)
482+
if err != nil {
483+
t.Fatalf(err.Error())
484+
}
485+
defer cancel()
486+
487+
c, out, err := cliCommand(env, "open", "test", "test2")
488+
if err != nil {
489+
t.Fatalf("failed to run CLI command: %v", err)
490+
}
491+
if c != 0 {
492+
t.Fatalf("exit code %d: %s", c, out)
493+
}
494+
out = stripANSI(out)
495+
if !strings.Contains(strings.ToLower(out), "opened tunnel 'test'") ||
496+
!strings.Contains(strings.ToLower(out), "opened tunnel 'test2'") {
497+
t.Errorf("output did not indicate success: %s", out)
498+
}
499+
500+
testTunnel(t, "localhost:49711", "localhost:49712")
501+
testTunnel(t, "localhost:49713", "localhost:49714")
502+
}
503+
453504
// Test a reverse connection
454505
func TestTunnelRemote(t *testing.T) {
455506
env, cancel, err := makeDefaultEnvWithDaemon(t)

0 commit comments

Comments
 (0)