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	"errors"
10	"net"
11	"path/filepath"
12	"strings"
13	"time"
14
15	"github.com/keybase/client/go/logger"
16	mspipe "github.com/keybase/go-winio"
17)
18
19func NewSocket(g *GlobalContext) (ret Socket, err error) {
20	var s string
21	s, err = g.Env.GetSocketBindFile()
22	if err != nil {
23		return
24	}
25	if len(s) == 0 {
26		err = errors.New("Empty SocketFile, can't make pipe")
27		return
28	}
29	s = `\\.\pipe\kbservice` + strings.TrimPrefix(s, filepath.VolumeName(s))
30	log := g.Log
31	if log == nil {
32		log = logger.NewNull()
33	}
34
35	// ownership tests fail when server is in same proces, as in tests
36	return SocketInfo{
37		log:       log,
38		bindFile:  s,
39		dialFiles: []string{s},
40		testOwner: g.Env.Test == nil,
41	}, nil
42}
43
44func NewSocketWithFiles(
45	log logger.Logger, bindFile string, _ []string) Socket {
46	s := `\\.\pipe\kbservice` +
47		strings.TrimPrefix(bindFile, filepath.VolumeName(bindFile))
48	return SocketInfo{
49		log:       log,
50		bindFile:  s,
51		dialFiles: []string{s},
52	}
53}
54
55func (s SocketInfo) BindToSocket() (ret net.Listener, err error) {
56	s.log.Info("Binding to pipe:%s", s.bindFile)
57	return mspipe.ListenPipe(s.bindFile, nil)
58}
59
60func (s SocketInfo) DialSocket() (ret net.Conn, err error) {
61	timeout := time.Duration(1) * time.Second
62	pipe, err := mspipe.DialPipe(s.dialFiles[0], &timeout)
63	if err != nil {
64		// Be sure to return a nil interface, and not a nil npipe.PipeConn
65		// See https://keybase.atlassian.net/browse/CORE-2675 for when this
66		// bit us.
67		return nil, err
68	}
69	// This can't happen right now, but in the future it might, so protect against ourselves
70	// so we don't get vexing (*foo)(nil)/interface{}(nil) bugs.
71	if pipe == nil {
72		return nil, errors.New("bad npipe result; nil npipe.PipeConn but no error")
73	}
74
75	// Test ownership
76	if s.testOwner {
77		owner, err := IsPipeowner(s.log, s.dialFiles[0])
78		if err != nil {
79			return nil, err
80		}
81		if !owner.IsOwner {
82			return nil, errors.New("failed to verify pipe ownership")
83		}
84	}
85	// Success case
86	return pipe, err
87}
88
89func IsSocketClosedError(e error) bool {
90	return e == mspipe.ErrPipeListenerClosed
91}
92