1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4// +build windows
5
6package libkb
7
8import (
9	"bufio"
10	"errors"
11	"fmt"
12	"os"
13	"os/exec"
14	"path/filepath"
15	"sync"
16	"testing"
17	"time"
18)
19
20func setupTest(t *testing.T, nm string) *TestContext {
21	tc := SetupTest(t, nm, 1)
22	tc.SetRuntimeDir(filepath.Join(tc.Tp.Home, "socket_windows_test"))
23	if err := tc.G.ConfigureSocketInfo(); err != nil {
24		t.Fatal(err)
25	}
26	return &tc
27}
28
29// It would be better to test across process boundaries, but this is better
30// than nothing: across gofuncs. We start a server func, then send it a string,
31// then synchronize with the server func.
32//
33// Another property of named pipes that is NOT tested here is security:
34// only processes in the same user account are supposed to be able to
35// open each other's named pipes.
36func TestWindowsNamedPipe(t *testing.T) {
37	tc := setupTest(t, "socket_windows_test")
38	defer tc.Cleanup()
39
40	listenSocket, err := NewSocket(tc.G)
41	if err != nil {
42		t.Fatal(err)
43	}
44
45	l, err := listenSocket.BindToSocket()
46	if err != nil {
47		t.Fatal(err)
48	}
49
50	// Do the server listening in a separate gofunc, which we synchronize
51	// with later after it has gotten a string
52	var wg sync.WaitGroup
53	wg.Add(1)
54	go func() {
55		defer wg.Done()
56		conn, err := l.Accept()
57		if err != nil {
58			t.Fatal(err)
59		}
60		answer, err := bufio.NewReader(conn).ReadString('\n')
61		if err != nil {
62			t.Fatal(err)
63		}
64		if answer != "Hi server!\n" {
65			t.Fatalf("Bad response over pipe: -%s-", answer)
66		}
67	}()
68
69	sendSocket, err := NewSocket(tc.G)
70	namedPipeClient(sendSocket, t)
71	wg.Wait()
72}
73
74// Dial the server over the pipe and send a string
75func namedPipeClient(sendSocket Socket, t *testing.T) {
76	conn, err := sendSocket.DialSocket()
77	if err != nil {
78		t.Fatal(err)
79	}
80	if _, err := fmt.Fprintln(conn, "Hi server!"); err != nil {
81		t.Fatal(err)
82	}
83}
84
85func TestWindowsPipeOwner(t *testing.T) {
86
87	if os.Getenv("JENKINS_URL") != "" {
88		t.Skip("Skipping pipeowner test - doesn't work on CI, works locally")
89	}
90
91	tc := setupTest(t, "socket_windows_test")
92	defer tc.Cleanup()
93
94	testPipeName := "\\\\.\\pipe\\kbservice\\test_pipe"
95	serverCmd := exec.Command("go", "run", "testfixtures\\kb_pipetest_server\\main.go", testPipeName)
96	err := serverCmd.Start()
97	if err != nil {
98		t.Fatal(err)
99	}
100	defer serverCmd.Process.Kill()
101
102	for i := 0; i < 20; i++ {
103		// Give the server time to open the pipe
104		time.Sleep(500 * time.Millisecond)
105
106		// Test existing pipe
107		owner, err := IsPipeowner(tc.G.Log, testPipeName)
108		if err != nil {
109			if i < 19 {
110				continue
111			}
112			t.Fatal(err)
113		}
114		if !owner.IsOwner {
115			t.Fatal(errors.New("Expected true getting owner of test pipe"))
116		}
117	}
118
119	// Test nonexisting
120	owner, err := IsPipeowner(tc.G.Log, testPipeName+"_nonexistent")
121	if err == nil {
122		t.Fatal(errors.New("Expected error getting owner of nonexistent pipe"))
123	}
124	if owner.IsOwner {
125		t.Fatal(errors.New("Expected false getting owner of nonexistent pipe"))
126	}
127}
128